Skip to content

Commit

Permalink
shop: basic purchase support
Browse files Browse the repository at this point in the history
  • Loading branch information
hex-agon committed May 11, 2024
1 parent 5f65a0c commit bfcbf38
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,38 @@ package work.fking.pangya.game.model

import io.netty.buffer.ByteBuf

const val IFF_TYPE_CHARACTER = 4
const val IFF_TYPE_PART = 8
const val IFF_TYPE_CLUBSET = 16
const val IFF_TYPE_BALL = 20
const val IFF_TYPE_EQUIPITEM_ITEM = 24
const val IFF_TYPE_NOEQUIP_ITEM = 26
const val IFF_TYPE_CADDIE = 28
const val IFF_TYPE_CARD = 124
enum class IffType {
CHARACTER,
PART,
CLUBSET,
BALL,
EQUIPITEM_ITEM,
NOEQUIP_ITEM,
CADDIE,
CARD
}

interface IffObject {

val uid: Int

val iffId: Int

fun iffTypeId(): Int {
return iffTypeFromId(iffId)
}
fun iffTypeId() = iffTypeFromId(iffId)

fun encode(buffer: ByteBuf)
}

fun iffTypeFromId(iffId: Int): Int {
return iffId shr 24
fun iffTypeFromId(iffId: Int): IffType {
return when (val typeId = iffId shr 24) {
4 -> IffType.CHARACTER
8 -> IffType.PART
16 -> IffType.CLUBSET
20 -> IffType.BALL
24 -> IffType.EQUIPITEM_ITEM
26 -> IffType.NOEQUIP_ITEM
28 -> IffType.CADDIE
124 -> IffType.CARD
else -> throw IllegalArgumentException("Unknown iffTypeId $typeId")
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package work.fking.pangya.game.net

import work.fking.pangya.game.packet.handler.AchievementStatusRequestPacketHandler
import work.fking.pangya.game.packet.handler.clubworkshop.ClubSetUpgradePacketHandler
import work.fking.pangya.game.packet.handler.EquipmentUpdatePacketHandler
import work.fking.pangya.game.packet.handler.JoinLobbyPacketHandler
import work.fking.pangya.game.packet.handler.LeaveLobbyPacketHandler
Expand All @@ -13,8 +12,10 @@ import work.fking.pangya.game.packet.handler.MyRoomOpenedPacketHandler
import work.fking.pangya.game.packet.handler.PapelShopPlayPacketHandler
import work.fking.pangya.game.packet.handler.RareShopOpenPacketHandler
import work.fking.pangya.game.packet.handler.SelectChannelPacketHandler
import work.fking.pangya.game.packet.handler.shop.ShopPurchaseHandler
import work.fking.pangya.game.packet.handler.UpdateChatMacrosPacketHandler
import work.fking.pangya.game.packet.handler.UserProfileRequestPacketHandler
import work.fking.pangya.game.packet.handler.clubworkshop.ClubSetUpgradePacketHandler
import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopAcceptTransformPacketHandler
import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopDeclineTransformPacketHandler
import work.fking.pangya.game.packet.handler.clubworkshop.ClubWorkshopRankUpPacketHandler
Expand All @@ -36,6 +37,7 @@ import work.fking.pangya.game.packet.handler.room.CreateRoomPacketHandler
import work.fking.pangya.game.packet.handler.room.JoinRoomHandler
import work.fking.pangya.game.packet.handler.room.LeaveRoomPacketHandler
import work.fking.pangya.game.packet.handler.room.RoomSettingsUpdatePacketHandler
import work.fking.pangya.game.packet.handler.shop.ShopEnterUnknownHandler

enum class ClientPacketType(
private val id: Int,
Expand All @@ -60,6 +62,7 @@ enum class ClientPacketType(
MATCH_HOLE_START(0x1a, MatchHoleStartPacketHandler()),
MATCH_PLAYER_SHOT_SYNC(0x1b, MatchPlayerShotSyncPacketHandler()),
MATCH_PLAYER_SHOT_END(0x1c, MatchPlayerTurnEndPacketHandler()),
SHOP_PURCHASE(0x1d, ShopPurchaseHandler()),
EQUIPMENT_UPDATE_IN_MY_ROOM(0x20, EquipmentUpdatePacketHandler()),
MATCH_PLAYER_TURN_START(0x22),
USER_PROFILE_REQUEST(0x2f, UserProfileRequestPacketHandler()),
Expand All @@ -80,7 +83,7 @@ enum class ClientPacketType(
UNKNOWN_C1(0xC1),
LOCKER_INVENTORY_REQUEST(0xd3, LockerInventoryRequestPacketHandler()),
MATCH_PLAYER_QUIT(0x130, MatchPlayerQuitPacketHandler()),
UNKNOWN_140(0x140),
SHOP_ENTER_UNKNOWN(0x140, ShopEnterUnknownHandler()),
HOLE_REPEAT_CHANGE_WIND(0x141),
ACHIEVEMENT_STATUS_REQUEST(0x157, AchievementStatusRequestPacketHandler()),
PLAYER_RING_PROC(0x15d),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package work.fking.pangya.game.packet.handler.clubworkshop

import io.netty.buffer.ByteBuf
import work.fking.pangya.game.GameServer
import work.fking.pangya.game.model.IFF_TYPE_CLUBSET
import work.fking.pangya.game.model.iffTypeFromId
import work.fking.pangya.game.net.ClientPacketHandler
import work.fking.pangya.game.packet.outbound.ClubSetReplies
import work.fking.pangya.game.player.Player
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package work.fking.pangya.game.packet.handler.shop

import io.netty.buffer.ByteBuf
import work.fking.pangya.game.GameServer
import work.fking.pangya.game.net.ClientPacketHandler
import work.fking.pangya.game.packet.outbound.ShopReplies
import work.fking.pangya.game.player.Player

class ShopEnterUnknownHandler: ClientPacketHandler {
override fun handle(server: GameServer, player: Player, packet: ByteBuf) {
player.writeAndFlush(ShopReplies.shopUnknownReply())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package work.fking.pangya.game.packet.handler.shop

import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufUtil
import org.slf4j.LoggerFactory
import work.fking.pangya.game.GameServer
import work.fking.pangya.game.model.IffType
import work.fking.pangya.game.model.iffTypeFromId
import work.fking.pangya.game.net.ClientPacketHandler
import work.fking.pangya.game.packet.outbound.PurchaseFailedReason.ALREADY_HAVE_THAT_ITEM
import work.fking.pangya.game.packet.outbound.ShopReplies
import work.fking.pangya.game.player.Player
import work.fking.pangya.game.task.ShopUnlockCharacterTask

private val LOGGER = LoggerFactory.getLogger(ShopPurchaseHandler::class.java)

class ShopPurchaseHandler : ClientPacketHandler {

override fun handle(server: GameServer, player: Player, packet: ByteBuf) {
println(ByteBufUtil.prettyHexDump(packet))
val rental = packet.readByte()
val numOfItems = packet.readUnsignedShortLE()

repeat(numOfItems) {
val uid = packet.readIntLE()
val iffId = packet.readIntLE()
val rentalTime = packet.readUnsignedShortLE()
val unknown = packet.readUnsignedShortLE()
val quantity = packet.readIntLE()
val pangCost = packet.readIntLE()
val cookieCost = packet.readIntLE()
val couponId = packet.readIntLE()

when (iffTypeFromId(iffId)) {
IffType.CHARACTER -> handleCharacterPurchase(server, player, iffId)
else -> handleUnknownPurchase(player, iffId)
}
}
}

private fun handleCharacterPurchase(server: GameServer, player: Player, iffId: Int) {
if (player.characterRoster.findByIffId(iffId) != null) {
player.writeAndFlush(ShopReplies.purchaseFailed(ALREADY_HAVE_THAT_ITEM))
return
}

server.runTask(
ShopUnlockCharacterTask(
server.persistenceCtx,
player,
iffId
)
)
}

private fun handleUnknownPurchase(player: Player, iffId: Int) {
LOGGER.warn("Player tried to purchase iffId={} type={} but we don't know how to handle it", iffId, iffTypeFromId(iffId))
player.writeAndFlush(ShopReplies.purchaseFailed())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package work.fking.pangya.game.packet.outbound

import work.fking.pangya.game.packet.outbound.PurchaseFailedReason.GENERIC_ERROR
import work.fking.pangya.game.player.PlayerWallet
import work.fking.pangya.networking.protocol.OutboundPacket

enum class PurchaseFailedReason(val code: Int) {
GENERIC_ERROR(1),
NOT_ENOUGH_PANG(2),
WRONG_ITEM_CODE(3),
ALREADY_HAVE_THAT_ITEM(4),
CHECK_ITEM_TIME_LIMIT(9), // this is maybe when trying to rent an item that is already rented
CHECK_SALE_TIMES(11)
}

object ShopReplies {

fun purchaseFailed(reason: PurchaseFailedReason = GENERIC_ERROR): OutboundPacket {
return OutboundPacket { buffer ->
buffer.writeShortLE(0x68)
buffer.writeIntLE(reason.code) // 1 = purchase failed, 2 = not enough pang, 3 = wrong item code, 4 = already have that item, 9 = check time limit of your item, 11 = please check sale times
}
}

fun purchaseSuccessful(wallet: PlayerWallet): OutboundPacket {
return OutboundPacket { buffer ->
buffer.writeShortLE(0x68)
buffer.writeIntLE(0)
buffer.writeLongLE(wallet.pangBalance)
buffer.writeLongLE(wallet.cookieBalance)
}
}

fun shopUnknownReply(): OutboundPacket {
return OutboundPacket { buffer ->
buffer.writeShortLE(0x20e)
buffer.writeIntLE(0) // maybe this is a timestamp to sync sales
buffer.writeIntLE(0)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package work.fking.pangya.game.player

import io.netty.buffer.ByteBuf
import work.fking.pangya.game.model.IFF_TYPE_BALL
import work.fking.pangya.game.model.IFF_TYPE_CLUBSET
import work.fking.pangya.game.model.IFF_TYPE_EQUIPITEM_ITEM
import work.fking.pangya.game.model.IffType.BALL
import work.fking.pangya.game.model.IffType.CLUBSET
import work.fking.pangya.game.model.IffType.EQUIPITEM_ITEM
import work.fking.pangya.game.model.iffTypeFromId

const val EQUIPPED_ITEMS_SIZE = 10
Expand All @@ -28,18 +28,18 @@ class Equipment(
require(itemIffIds.size == EQUIPPED_ITEMS_SIZE) { "Equipped item iff ids invalid length" }
for (i in itemIffIds.indices) {
val iffId = itemIffIds[i]
require(iffId == 0 || iffTypeFromId(iffId) == IFF_TYPE_EQUIPITEM_ITEM) { "Iff object $iffId is not an item" }
require(iffId == 0 || iffTypeFromId(iffId) == EQUIPITEM_ITEM) { "Iff object $iffId is not an item" }
this.itemIffIds[i] = iffId
}
}

fun equipClubSet(clubSet: Item) {
require(clubSet.iffTypeId() == IFF_TYPE_CLUBSET) { "Item is not a clubSet" }
require(clubSet.iffTypeId() == CLUBSET) { "Item is not a clubSet" }
clubSetUid = clubSet.uid
}

fun equipComet(comet: Item) {
require(comet.iffTypeId() == IFF_TYPE_BALL) { "Item is not a comet" }
require(comet.iffTypeId() == BALL) { "Item is not a comet" }
cometIffId = comet.iffId
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package work.fking.pangya.game.player

import io.netty.buffer.ByteBuf
import work.fking.pangya.game.model.IFF_TYPE_BALL
import work.fking.pangya.game.model.IFF_TYPE_EQUIPITEM_ITEM
import work.fking.pangya.game.model.IFF_TYPE_NOEQUIP_ITEM
import work.fking.pangya.game.model.IffObject
import work.fking.pangya.game.model.IffType.BALL
import work.fking.pangya.game.model.IffType.EQUIPITEM_ITEM
import work.fking.pangya.game.model.IffType.NOEQUIP_ITEM
import work.fking.pangya.game.model.iffTypeFromId

class Item(
Expand All @@ -19,7 +19,7 @@ class Item(

fun quantifiable(): Boolean {
return when (iffTypeFromId(iffId)) {
IFF_TYPE_EQUIPITEM_ITEM, IFF_TYPE_NOEQUIP_ITEM, IFF_TYPE_BALL -> true
EQUIPITEM_ITEM, NOEQUIP_ITEM, BALL -> true
else -> false
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package work.fking.pangya.game.task

import work.fking.pangya.game.model.IFF_TYPE_CLUBSET
import work.fking.pangya.game.model.IffType.CLUBSET
import work.fking.pangya.game.model.iffTypeFromId
import work.fking.pangya.game.packet.outbound.ClubSetReplies
import work.fking.pangya.game.packet.outbound.ClubSetReplies.UpgradeResult.CANNOT_DOWNGRADE_ANYMORE
Expand Down Expand Up @@ -31,7 +31,7 @@ class ChangeClubSetStatTask(

override fun run() {
val clubSet = player.inventory.findByUid(itemUid) ?: throw IllegalStateException("Player ${player.nickname} tried to upgrade an item it does not own")
require(iffTypeFromId(clubSet.iffId) == IFF_TYPE_CLUBSET) { "Player ${player.nickname} tried to upgrade an item that is not a ClubSet" }
require(iffTypeFromId(clubSet.iffId) == CLUBSET) { "Player ${player.nickname} tried to upgrade an item that is not a ClubSet" }

when (type) {
TYPE_UPGRADE_CLUBSET -> upgrade(clubSet)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package work.fking.pangya.game.task

import work.fking.pangya.game.model.IFF_TYPE_CHARACTER
import work.fking.pangya.game.model.IffType.CHARACTER
import work.fking.pangya.game.model.iffTypeFromId
import work.fking.pangya.game.packet.outbound.ShopReplies
import work.fking.pangya.game.persistence.PersistenceContext
import work.fking.pangya.game.player.Character
import work.fking.pangya.game.player.Player
import work.fking.pangya.game.player.characterBaseParts

class UnlockCharacterTask(
class ShopUnlockCharacterTask(
private val persistenceCtx: PersistenceContext,
private val player: Player,
private val iffId: Int
) : Runnable {

override fun run() {
require(iffTypeFromId(iffId) == IFF_TYPE_CHARACTER) { "iffId is not a character" }
require(iffTypeFromId(iffId) == CHARACTER) { "iffId is not a character" }
val partIffIds = characterBaseParts(iffId)
val character = persistenceCtx.characterRepository.save(
txCtx = persistenceCtx.noTxContext(),
Expand All @@ -25,5 +26,6 @@ class UnlockCharacterTask(
)
)
player.characterRoster.entries.add(character)
player.writeAndFlush(ShopReplies.purchaseSuccessful(player.wallet))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package work.fking.pangya.game.task

import work.fking.pangya.game.model.IFF_TYPE_CADDIE
import work.fking.pangya.game.model.IffType.CADDIE
import work.fking.pangya.game.model.iffTypeFromId
import work.fking.pangya.game.persistence.PersistenceContext
import work.fking.pangya.game.player.Caddie
Expand All @@ -13,7 +13,7 @@ class UnlockCaddieTask(
) : Runnable {

override fun run() {
require(iffTypeFromId(iffId) == IFF_TYPE_CADDIE) { "iffId is not a caddie" }
require(iffTypeFromId(iffId) == CADDIE) { "iffId is not a caddie" }
val caddie = Caddie(iffId = iffId)
val persistedCaddie = persistenceCtx.caddieRepository.save(persistenceCtx.noTxContext(), player.uid, caddie)
player.caddieRoster.entries.add(persistedCaddie)
Expand Down

0 comments on commit bfcbf38

Please sign in to comment.