From 61472934312cb74a50102e3a38b915abd62e0c43 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Mon, 31 Jul 2023 03:03:03 +0000 Subject: [PATCH] Improved the performance of drop table rolling Chromatic Dragon eggs now only drop at a 1/1000 rate if the player has 99 summoning, impossible to obtain otherwise --- .../handlers/npc/ChromaticDragonBehavior.kt | 43 ++++++++++++++++ .../src/main/core/api/utils/NPCDropTable.kt | 6 +-- .../main/core/api/utils/WeightBasedTable.kt | 37 +++++++------- .../node/entity/combat/CombatSwingHandler.kt | 4 +- .../node/entity/npc/drop/NPCDropTables.java | 8 ++- .../command/sets/DevelopmentCommandSet.kt | 9 +--- .../src/main/core/tools/RandomFunction.java | 1 + .../src/test/kotlin/content/DropTableTests.kt | 49 +++++++++++++++++++ 8 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt create mode 100644 Server/src/test/kotlin/content/DropTableTests.kt diff --git a/Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt b/Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt new file mode 100644 index 000000000..57c0fda75 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt @@ -0,0 +1,43 @@ +package content.global.handlers.npc + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.* +import kotlin.collections.ArrayList + +class ChromaticDragonBehavior : NPCBehavior(*greenDragons, *blueDragons, *redDragons, *blackDragons) +{ + override fun onDropTableRolled (self: NPC, killer: Entity, drops: ArrayList) { + val removeList = ArrayList() + for (item in drops) { + when (item.id) { + Items.BLACK_DRAGON_EGG_12480, + Items.RED_DRAGON_EGG_12477, + Items.BLUE_DRAGON_EGG_12478, + Items.GREEN_DRAGON_EGG_12479 -> removeList.add(item) + } + } + drops.removeAll(removeList) + + if (killer.skills.getStaticLevel(Skills.SUMMONING) >= 99 && RandomFunction.roll(EGG_RATE)) + drops.add(when(self.id) { + in greenDragons -> Item(Items.GREEN_DRAGON_EGG_12479) + in blueDragons -> Item(Items.BLUE_DRAGON_EGG_12478) + in redDragons -> Item(Items.RED_DRAGON_EGG_12477) + in blackDragons -> Item(Items.BLACK_DRAGON_EGG_12480) + else -> Item(Items.DRAGON_BONES_536) + }) + } + + companion object { + val greenDragons = intArrayOf (NPCs.GREEN_DRAGON_941, NPCs.GREEN_DRAGON_4677, NPCs.GREEN_DRAGON_4678, NPCs.GREEN_DRAGON_4679, NPCs.GREEN_DRAGON_4680) + val blueDragons = intArrayOf (NPCs.BLUE_DRAGON_55, NPCs.BLUE_DRAGON_4681, NPCs.BLUE_DRAGON_4682, NPCs.BLUE_DRAGON_4683, NPCs.BLUE_DRAGON_4684) + val redDragons = intArrayOf (NPCs.RED_DRAGON_53, NPCs.RED_DRAGON_4669, NPCs.RED_DRAGON_4670, NPCs.RED_DRAGON_4671, NPCs.RED_DRAGON_4672) + val blackDragons = intArrayOf (NPCs.BLACK_DRAGON_54, NPCs.BLACK_DRAGON_4673, NPCs.BLACK_DRAGON_4674, NPCs.BLACK_DRAGON_4675, NPCs.BLACK_DRAGON_4676) + var EGG_RATE = 1000 + } +} diff --git a/Server/src/main/core/api/utils/NPCDropTable.kt b/Server/src/main/core/api/utils/NPCDropTable.kt index 05e1f96be..80b19f9b4 100644 --- a/Server/src/main/core/api/utils/NPCDropTable.kt +++ b/Server/src/main/core/api/utils/NPCDropTable.kt @@ -10,12 +10,12 @@ class NPCDropTable : WeightBasedTable() { return charmDrops.add(element) } - override fun roll(receiver: Entity?): ArrayList { + override fun roll(receiver: Entity?, times: Int): ArrayList { val items = ArrayList() // Charms table is always rolled, and should contain explicit "Nothing" // entries at the data level to account for the chance to not drop a charm. - items.addAll(charmDrops.roll()) - items.addAll(super.roll(receiver)) + items.addAll(charmDrops.roll(receiver, times)) + items.addAll(super.roll(receiver, times)) return items } diff --git a/Server/src/main/core/api/utils/WeightBasedTable.kt b/Server/src/main/core/api/utils/WeightBasedTable.kt index c3a0acc50..79466bffe 100644 --- a/Server/src/main/core/api/utils/WeightBasedTable.kt +++ b/Server/src/main/core/api/utils/WeightBasedTable.kt @@ -38,29 +38,26 @@ open class WeightBasedTable : ArrayList() { } } - fun roll(receiver: Entity? = null, rollCount: Int) : ArrayList { - val items = ArrayList() - - for (i in 0 until rollCount) - items.addAll(roll(receiver)) - - return items + open fun roll(receiver: Entity? = null) : ArrayList { + return roll(receiver, 1) } - open fun roll(receiver: Entity? = null): ArrayList{ - val items = ArrayList() - items.addAll(guaranteedItems) + open fun roll(receiver: Entity? = null, times: Int = 1): ArrayList{ + val items = ArrayList((guaranteedItems.size + 1) * times) - if (size == 1) { - items.add(get(0)) - } - else if (isNotEmpty()) { - var rngWeight = RandomFunction.randomDouble(totalWeight) - for (item in this) { - rngWeight -= item.weight - if (rngWeight <= 0) { - items.add(item) - break + for (i in 0 until times) { + items.addAll(guaranteedItems) + + if (size == 1) { + items.add(get(0)) + } else if (isNotEmpty()) { + var rngWeight = RandomFunction.randomDouble(totalWeight) + for (item in this) { + rngWeight -= item.weight + if (rngWeight <= 0) { + items.add(item) + break + } } } } diff --git a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt index c2fcfce0d..e0c99413e 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -31,7 +31,9 @@ import kotlin.math.floor * @author Emperor * @author Ceikry - Kotlin refactoring, general cleanup */ -abstract class CombatSwingHandler(var type: CombatStyle?, vararg val flags: SwingHandlerFlag) { +abstract class CombatSwingHandler(var type: CombatStyle?) { + var flags: Array = emptyArray() + constructor(type: CombatStyle?, vararg flags: SwingHandlerFlag) : this(type) { this.flags = flags } /** * The mapping of the special attack handlers. */ diff --git a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java index 5d545bc69..31397a4c4 100644 --- a/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java @@ -74,11 +74,17 @@ public final class NPCDropTables { */ public void drop(NPC npc, Entity looter) { Player p = looter instanceof Player ? (Player) looter : null; - ArrayList drops = table.roll(looter); + ArrayList drops = table.roll(looter, 1); npc.behavior.onDropTableRolled(npc, looter, drops); drops.forEach(item -> createDrop(item,p,npc,npc.getDropLocation())); } + public List roll (NPC npc, Entity looter, int times) { + ArrayList drops = table.roll(looter, times); + npc.behavior.onDropTableRolled(npc, looter, drops); + return drops; + } + /** * Creates a dropped item. * @param item The item to drop. diff --git a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt index 879fc0425..f32d5945f 100644 --- a/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt @@ -167,13 +167,8 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { val amount = args[2].toInt() container.clear() - - for(i in 0..amount) - { - val drops = NPCDefinition.forId(npcId).dropTables.table.roll(player) - for(drop in drops) container.add(drop) - } - + val drops = NPCDefinition.forId(npcId).dropTables.table.roll(player, amount) + for(drop in drops) container.add(drop, false) container.open(player) } diff --git a/Server/src/main/core/tools/RandomFunction.java b/Server/src/main/core/tools/RandomFunction.java index 3cd1fa0e6..7e8dd580f 100644 --- a/Server/src/main/core/tools/RandomFunction.java +++ b/Server/src/main/core/tools/RandomFunction.java @@ -50,6 +50,7 @@ public class RandomFunction { * @return true if you hit the roll, false otherwise */ public static boolean roll(int chance){ + if (chance <= 1) return true; return random(chance + 1) == chance / 2; } diff --git a/Server/src/test/kotlin/content/DropTableTests.kt b/Server/src/test/kotlin/content/DropTableTests.kt new file mode 100644 index 000000000..001524cd0 --- /dev/null +++ b/Server/src/test/kotlin/content/DropTableTests.kt @@ -0,0 +1,49 @@ +package content + +import TestUtils +import content.global.handlers.npc.ChromaticDragonBehavior +import core.ServerConstants +import core.api.utils.NPCDropTable +import core.api.utils.WeightedItem +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class DropTableTests { + init { + TestUtils.preTestSetup() + } + @Test fun chromaticDragonsShouldDropEggsOnlyAfter99Summoning() { + val npc = NPC.create(NPCs.BLACK_DRAGON_54, ServerConstants.HOME_LOCATION) + val oldTable = npc.definition.dropTables.table + + val testTable = NPCDropTable() //manufacture a fake table that's guaranteed to give an egg, for testing safety. + testTable.add(WeightedItem(Items.BLACK_DRAGON_EGG_12480, 1, 1, 1.0)) + + npc.definition.dropTables.table = testTable + npc.behavior = ChromaticDragonBehavior() + + TestUtils.getMockPlayer("chromaticeggdrop").use { p -> + ChromaticDragonBehavior.EGG_RATE = 1 //guarantee egg drop if other parameters are satisfied + var hasEgg = false + + var items = npc.definition.dropTables.roll(npc, p, 1) + for (item in items) + if (item.id == Items.BLACK_DRAGON_EGG_12480) hasEgg = true + Assertions.assertEquals(false, hasEgg) + + p.skills.setStaticLevel(Skills.SUMMONING, 99) + + items = npc.definition.dropTables.roll(npc, p, 1) + for (item in items) + if (item.id == Items.BLACK_DRAGON_EGG_12480) hasEgg = true + Assertions.assertEquals(true, hasEgg) + } + + npc.definition.dropTables.table = oldTable + } +} \ No newline at end of file