Merge branch 'valid-shop-repairs' into 'master'

fixes #2721

Closes #2721

See merge request 2009scape/2009scape!2484
This commit is contained in:
Tooze 2026-07-31 21:12:14 +00:00
commit 51f75d176e
3 changed files with 51 additions and 14 deletions

View file

@ -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)
}

View file

@ -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 {

View file

@ -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.
*/