adjusted price calculation formula used when selling items to shops
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user