adjusted price calculation formula used when selling items to shops

This commit is contained in:
Ryan
2022-05-06 22:54:32 +00:00
committed by Ceikry
parent 2b0c35a420
commit d91396d0ea
3 changed files with 185 additions and 40 deletions
@@ -15,11 +15,11 @@ import org.rs09.consts.Components
import org.rs09.consts.Items
import rs09.ServerConstants
import rs09.game.content.global.shops.Shops.Companion.logShop
import rs09.game.system.SystemLogger
import rs09.game.world.GameWorld
import java.lang.Integer.max
import java.lang.Integer.min
import kotlin.math.ceil
import kotlin.math.roundToInt
data class ShopItem(var itemId: Int, var amount: Int, val restockRate: Int = 100)
@@ -168,7 +168,14 @@ class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean =
{
val shopCont = getAttribute<Container?>(player, "shop-cont", null) ?: return Pair(null, Item(-1,-1))
val item = player.inventory[slot]
var (isPlayerStock, shopSlot) = getStockSlot(item.id)
val shopItemId = if (item.definition.isUnnoted) {
item.id
}
else
{
item.noteChange
}
var (isPlayerStock, shopSlot) = getStockSlot(shopItemId)
val stockAmt =
if(isPlayerStock)
@@ -178,7 +185,7 @@ class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean =
else 0
}
val currentAmt =
if(isPlayerStock) playerStock.getAmount(item.id)
if(isPlayerStock) playerStock.getAmount(shopItemId)
else {
if(shopSlot != -1) shopCont[shopSlot].amount
else {
@@ -191,7 +198,7 @@ class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean =
{
Items.TOKKUL_6529 -> item.definition.getConfiguration("tokkul_price", 1)
Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration("archery_ticket_price", 1)
else -> getGPSell(Item(item.id, 1), stockAmt, currentAmt)
else -> getGPSell(Item(shopItemId, 1), stockAmt, currentAmt)
}
if(!general && stockAmt == 0 && shopSlot == -1)
@@ -202,7 +209,8 @@ class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean =
return Pair(if (isPlayerStock) playerStock else shopCont, Item(currency, price))
}
private fun getGPCost(item: Item, stockAmount: Int, currentAmt: Int): Int{
private fun getGPCost(item: Item, stockAmount: Int, currentAmt: Int): Int
{
var mod: Int
mod = if(stockAmount == 0) 100
else if(currentAmt == 0) 130
@@ -219,22 +227,21 @@ class Shop(val title: String, val stock: Array<ShopItem>, val general: Boolean =
return max(price, 1)
}
private fun getGPSell(item: Item, stockAmount: Int, currentAmt: Int): Int{
if(!item.definition.isUnnoted)
item.id = item.noteChange
var mod: Int
mod = if(stockAmount == 0) 70
else if(currentAmt == 0) 100
else if(currentAmt >= stockAmount) 70
else 100 - (100 - 70) * currentAmt / stockAmount
if(mod < 1) mod = 1
mod = max(70, min(100, mod))
var base = if (highAlch) item.definition.getAlchemyValue(true) else item.definition.value
base = max(base, item.definition.value)
val price: Int = ceil(base * mod.toDouble() / 100.0).toInt()
return max(price, 1)
private fun getGPSell(item: Item, stockAmount: Int, currentAmt: Int): Int
{
val base = item.definition.getAlchemyValue(highAlch)
var overstock = currentAmt - stockAmount
if (overstock < 0) {
return base
}
if (overstock > 10) {
overstock = 10
}
val price = (base - (item.definition.value * 0.03 * overstock)).roundToInt()
if (price < 1) {
return 1
}
return price
}
fun buy(player: Player, slot: Int, amount: Int) : TransactionStatus
+150 -17
View File
@@ -1,23 +1,45 @@
import core.game.node.entity.player.link.IronmanMode
import core.game.node.item.Item
import org.junit.Assert
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
import rs09.game.content.global.shops.Shop
import rs09.game.content.global.shops.Shops
import rs09.game.system.config.ShopParser
import kotlin.math.min
import kotlin.math.roundToInt
class ShopTests {
val testPlayer = TestUtils.getMockPlayer("test")
val testIronman = TestUtils.getMockPlayer("test2", IronmanMode.STANDARD)
val nonGeneral = TestUtils.getMockShop("Not General", false, Item(4151, 1))
val general = TestUtils.getMockShop("General", true, Item(4151, 1))
companion object {
init {TestUtils.preTestSetup()}
}
private val testPlayer = TestUtils.getMockPlayer("test")
private val testIronman = TestUtils.getMockPlayer("test2", IronmanMode.STANDARD)
var nonGeneral = TestUtils.getMockShop("Not General", false, false, Item(4151, 1))
var general = TestUtils.getMockShop("General", true, false, Item(4151, 1))
var highAlch = TestUtils.getMockShop("High(af) Alch", true, true, Item(4151, 1))
@BeforeEach fun beforeEach() {
val testPlayers = arrayOf(testPlayer, testIronman)
for (player in testPlayers) {
player.inventory.clear()
player.attributes.remove("shop-cont")
}
nonGeneral.playerStock.clear()
general.playerStock.clear()
highAlch.playerStock.clear()
}
private fun assertTransactionSuccess(status: Shop.TransactionStatus) {
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
}
@Test fun shouldSellItemToStore() {
testPlayer.inventory.add(Item(4151, 1))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
val status = general.sell(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failed: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldNotSellUnstockedItemToStandardStore() {
@@ -31,28 +53,139 @@ class ShopTests {
testPlayer.inventory.add(Item(1, 1))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
val status = general.sell(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldSellUnstockedItemToGeneralStoreUsingLowAlchBaseValue() {
//starts at 40% value - a.k.a. low alch price
//drops 3% value per item stocked
//bottoms out at 10% value (25% of the low alch price) after 10 items stocked
val saleItemId = Items.RUNE_MED_HELM_1147
val shopContainer = general.getContainer(testPlayer)
Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.")
testPlayer.setAttribute("shop-cont", shopContainer)
val playerStock = general.playerStock
Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.")
val saleItem = Item(saleItemId, 1)
val alchValue = saleItem.definition.getAlchemyValue(false)
val value = saleItem.definition.value
Assertions.assertEquals((value * 0.4).roundToInt(), alchValue, "Pre-assertion, low alch value should be 40% value.")
for (i in 0..14) {
val expectedCoins = (alchValue.toDouble() - value * min(0.03 * i, 0.30)).roundToInt()
testPlayer.inventory.add(saleItem.copy())
Assertions.assertEquals(saleItemId, testPlayer.inventory.getId(0), "Pre-assertion, should have item in inventory slot 0.")
val status = general.sell(testPlayer, 0, 1)
assertTransactionSuccess(status)
val coinItem = testPlayer.inventory[0]
Assertions.assertEquals(Items.COINS_995, coinItem.id)
Assertions.assertEquals(
expectedCoins,
coinItem.amount,
"Selling item $i should yield the expected price."
)
testPlayer.inventory.clear()
}
}
@Test fun shouldSellUnstockedItemToHighAlchStoreUsingHighAlchBaseValue() {
//starts at 60% value - a.k.a. high alch price
//drops 3% value per item stocked
//bottoms out at 30% value (50% of the high alch price) after 10 items stocked
val saleItemId = Items.RUNE_MED_HELM_1147
val shopContainer = highAlch.getContainer(testPlayer)
Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.")
testPlayer.setAttribute("shop-cont", shopContainer)
val playerStock = highAlch.playerStock
Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.")
val saleItem = Item(saleItemId, 1)
val alchValue = saleItem.definition.getAlchemyValue(true)
val value = saleItem.definition.value
Assertions.assertEquals((value * 0.6).roundToInt(), alchValue, "Pre-assertion, high alch value should be 60% value.")
for (i in 0..14) {
val expectedCoins = (alchValue.toDouble() - value * min(0.03 * i, 0.30)).roundToInt()
testPlayer.inventory.add(saleItem.copy())
Assertions.assertEquals(saleItemId, testPlayer.inventory.getId(0), "Pre-assertion, should have item in inventory slot 0.")
val status = highAlch.sell(testPlayer, 0, 1)
assertTransactionSuccess(status)
val coinItem = testPlayer.inventory[0]
Assertions.assertEquals(Items.COINS_995, coinItem.id)
Assertions.assertEquals(
expectedCoins,
coinItem.amount,
"Selling item $i should yield the expected price."
)
testPlayer.inventory.clear()
}
}
@Test fun shouldSellNotedUnstockedItemForSamePriceAsUnnoted() {
val saleItemId = Items.RUNE_MED_HELM_1147
var notedSaleItemId = Items.RUNE_MED_HELM_1148
val shopContainer = highAlch.getContainer(testPlayer)
Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.")
testPlayer.setAttribute("shop-cont", shopContainer)
val playerStock = highAlch.playerStock
Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.")
testPlayer.inventory.add(Item(saleItemId, 1))
var status = highAlch.sell(testPlayer, 0, 1)
assertTransactionSuccess(status)
testPlayer.inventory.clear()
val notedSaleItem = Item(notedSaleItemId, 1)
testPlayer.inventory.add(notedSaleItem)
val alchValue = notedSaleItem.definition.getAlchemyValue(true)
val value = notedSaleItem.definition.value
status = highAlch.sell(testPlayer, 0, 1)
assertTransactionSuccess(status)
val expectedCoins = (alchValue.toDouble() - value * 0.03).roundToInt()
val coinItem = testPlayer.inventory[0]
Assertions.assertEquals(Items.COINS_995, coinItem.id)
Assertions.assertEquals(
expectedCoins,
coinItem.amount,
"Selling noted item should yield the same devalued price as base item."
)
}
@Test fun minimumSellPriceShouldBe1Coin() {
testPlayer.inventory.add(Item(Items.EMPTY_POT_1931, 1))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
val status = general.sell(testPlayer, 0, 1)
assertTransactionSuccess(status)
val coinItem = testPlayer.inventory[0]
Assertions.assertEquals(Items.COINS_995, coinItem.id)
Assertions.assertEquals(
1,
coinItem.amount,
"1 coin should be the minimum selling price."
)
}
@Test fun shouldSellUnstockedItemToGeneralStoreAsIronman() {
testIronman.inventory.add(Item(1, 1))
testIronman.setAttribute("shop-cont", general.getContainer(testPlayer))
testIronman.setAttribute("shop-cont", general.getContainer(testIronman))
val status = general.sell(testIronman, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldSellStackOfUnstockedItemsToPlayerStock() {
testPlayer.inventory.add(Item(1, 20))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
val status = general.sell(testPlayer, 0, 20)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldPutSoldUnstockedItemsInPlayerStock() {
testPlayer.inventory.add(Item(2,1))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
val status = general.sell(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
Assertions.assertEquals(1, general.playerStock.getAmount(2))
Assertions.assertEquals(0, general.getContainer(testPlayer).getAmount(2))
}
@@ -62,7 +195,7 @@ class ShopTests {
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
testPlayer.setAttribute("shop-main", true)
val status = general.buy(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldAllowStandardPlayerToBuyOverstock() {
@@ -71,7 +204,7 @@ class ShopTests {
testPlayer.setAttribute("shop-main", true)
general.getContainer(testPlayer).add(Item(4151, 100))
val status = general.buy(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldAllowStandardPlayerToBuyPlayerStock() {
@@ -80,7 +213,7 @@ class ShopTests {
testPlayer.setAttribute("shop-main", false)
general.playerStock.add(Item(4151, 100))
val status = general.buy(testPlayer, 0, 1)
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}")
assertTransactionSuccess(status)
}
@Test fun shouldNotAllowIronmanToBuyOverstock() {
@@ -128,11 +261,11 @@ class ShopTests {
testPlayer.inventory.add(Item(995, 100000))
testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer))
testPlayer.setAttribute("shop-main", false)
var status: Shop.TransactionStatus = Shop.TransactionStatus.Success()
var status: Shop.TransactionStatus = Shop.TransactionStatus.Failure("Test did not assign transaction status.")
Assertions.assertDoesNotThrow({
status = general.buy(testPlayer, 36, 1)
}, "Error selling to shop: ${general.playerStock}")
Assertions.assertEquals(true, status is Shop.TransactionStatus.Success)
assertTransactionSuccess(status)
}
@Test fun invalidStockJsonShouldNotCauseItemShift() {
+7 -2
View File
@@ -22,8 +22,13 @@ object TestUtils {
return p
}
fun getMockShop(name: String, general: Boolean, vararg stock: Item) : Shop {
return Shop(name, stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(), general)
fun getMockShop(name: String, general: Boolean, highAlch: Boolean, vararg stock: Item) : Shop {
return Shop(
name,
stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(),
general,
highAlch = highAlch
)
}
fun preTestSetup() {