diff --git a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt index 04fe7686a..09e126d6a 100644 --- a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt +++ b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt @@ -73,8 +73,11 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { } val roots = entity.inventory.getAmount(Item(Items.OGLEROOT_12624)) if(roots > 0) { + val refund = calculateExchangeCost(entity, 10, roots, null) entity.inventory.remove(Item(Items.OGLEROOT_12624, roots)) - entity.inventory.add(Item(Items.COINS_995, roots * 10)) + if(refund != null) { + entity.inventory.add(Item(Items.COINS_995, refund.totalCost)) + } } } } @@ -332,19 +335,21 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { fun buy(player: Player, buttonID: Int, amount: Int) { val reward = REWARDS[buttonID] ?: return - val cost = amount * reward.points + val exchange = calculateExchangeCost(player, reward.points, amount, "You don't have enough points for that.") ?: return val points = player.getAttribute("vinesweeper:points", 0) - if(cost in 1 until points) { - val item = Item(reward.itemID, amount) - if(!player.inventory.add(item)) { - GroundItemManager.create(item, player) - } - player.incrementAttribute("/save:vinesweeper:points", -cost) - sendUpdatedPoints(player) - } else { - // TODO (crash): authenticity + + if (points < exchange.totalCost) { player.sendMessage("You don't have enough points for that.") + return } + + val item = Item(reward.itemID, exchange.amount) + if (!player.inventory.add(item)) { + GroundItemManager.create(item, player) + } + + player.incrementAttribute("/save:vinesweeper:points", -exchange.totalCost) + sendUpdatedPoints(player) } diff --git a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt index 23c012487..dbd5c1111 100644 --- a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt +++ b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt @@ -132,12 +132,14 @@ class BlinkinDialogue : FarmerDialogue() { 42 -> { sendInputDialogue(player!!, InputType.AMOUNT, "Enter an amount:") { value -> val amount = value as Int - val price = Item(Items.COINS_995, 10 * amount) - if(price.amount <= 0){ + val exchange = calculateExchangeCost(player!!, 10, amount, null) ?: run { + npcl("Sorry, ya can't afford that many. Come back when yer feeling a bit richer if ya like!") + stage = END_DIALOGUE return@sendInputDialogue } + val price = Item(Items.COINS_995, exchange.totalCost) if(player!!.inventory.containsItem(price) && player!!.inventory.remove(price)) { - if(player!!.inventory.add(Item(Items.OGLEROOT_12624, amount))) { + if(player!!.inventory.add(Item(Items.OGLEROOT_12624, exchange.amount))) { npcl("There ya go! Good luck!") stage = END_DIALOGUE } else { diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index 7423518a7..23494305c 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -1054,6 +1054,36 @@ fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { } } +data class ExchangeAmount(val amount: Int, val totalCost: Int) + +/** + * Safely calculates the total cost + * + * Prevents zero, negative, and int-overflow costs before callers remove currency + * or add rewards. The cost can be coins, points, tickets, tokul, tokens, etc. + * + * Returns the amount and total cost, or null if invalid. + */ +@JvmOverloads +fun calculateExchangeCost(player: Player,unitCost: Int,amount: Int,failureMessage: String? = "You don't have enough.",dialogue: Boolean = false): ExchangeAmount? { + if (unitCost <= 0 || amount <= 0) { + failureMessage?.let { + if (dialogue) sendDialogue(player, it) else sendMessage(player, it) + } + return null + } + + val totalCost = unitCost.toLong() * amount.toLong() + if (totalCost !in 1..Int.MAX_VALUE) { + failureMessage?.let { + if (dialogue) sendDialogue(player, it) else sendMessage(player, it) + } + return null + } + + return ExchangeAmount(amount, totalCost.toInt()) +} + /** * Closes any opened dialogue. */