From bf26faf2a2cce2490b928d16ae9216d072248d67 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Sun, 30 Jul 2023 21:57:51 +0000 Subject: [PATCH] Fixed damage scaling issue with chinchompas. --- .../special/ChinchompaSwingHandler.java | 9 +- .../node/entity/combat/CombatSwingHandler.kt | 9 +- .../node/entity/combat/MagicSwingHandler.kt | 11 +- .../node/entity/combat/MeleeSwingHandler.kt | 17 ++- .../node/entity/combat/RangeSwingHandler.kt | 19 ++- Server/src/test/kotlin/content/CombatTests.kt | 127 ++++++++++++++++++ 6 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 Server/src/test/kotlin/content/CombatTests.kt diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java index 6f962cfad..fea739a85 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java @@ -1,9 +1,7 @@ package content.global.handlers.item.equipment.special; import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.InteractionType; +import core.game.node.entity.combat.*; import core.game.node.entity.combat.equipment.Ammunition; import core.game.node.entity.combat.equipment.RangeWeapon; import core.game.node.entity.combat.equipment.Weapon; @@ -14,7 +12,6 @@ import core.game.node.entity.skill.Skills; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; -import core.game.node.entity.combat.RangeSwingHandler; import java.util.List; @@ -37,8 +34,8 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler { /** * Constructs a new {@code ChinchompaSwingHandler} {@code Object}. */ - private ChinchompaSwingHandler() { - super(); + public ChinchompaSwingHandler() { + super(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE); } @Override 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 a65b00d03..c2fcfce0d 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,7 @@ import kotlin.math.floor * @author Emperor * @author Ceikry - Kotlin refactoring, general cleanup */ -abstract class CombatSwingHandler(var type: CombatStyle?) { +abstract class CombatSwingHandler(var type: CombatStyle?, vararg val flags: SwingHandlerFlag) { /** * The mapping of the special attack handlers. */ @@ -660,3 +660,10 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { } } + +enum class SwingHandlerFlag { + IGNORE_STAT_BOOSTS_DAMAGE, + IGNORE_STAT_BOOSTS_ACCURACY, + IGNORE_PRAYER_BOOSTS_DAMAGE, + IGNORE_PRAYER_BOOSTS_ACCURACY +} diff --git a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt index 13d0164b8..41f422b6f 100644 --- a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt @@ -15,8 +15,8 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, Kotlin conversion + cleanup */ -open class MagicSwingHandler - : CombatSwingHandler(CombatStyle.MAGIC) { +open class MagicSwingHandler (vararg flags: SwingHandlerFlag) + : CombatSwingHandler(CombatStyle.MAGIC, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { if (!isProjectileClipped(entity, victim, false)) { @@ -151,12 +151,15 @@ open class MagicSwingHandler } val level = entity.skills.getLevel(Skills.MAGIC, true) var prayer = 1.0 - if (entity is Player) { + if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) { prayer += entity.prayer.getSkillBonus(Skills.MAGIC) } val additional = getSetMultiplier(entity, Skills.MAGIC); val effective = floor(level * prayer * additional + spellBonus) - val bonus = entity.properties.bonuses[WeaponInterface.BONUS_MAGIC] + val bonus = + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + entity.properties.bonuses[WeaponInterface.BONUS_MAGIC] + else 0 return floor((effective + 8) * (bonus + 64) / 10).toInt() } diff --git a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt index 7dddb9576..dc27df980 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -24,11 +24,11 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, Kotlin conversion + cleanup */ -open class MeleeSwingHandler +open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) /** * Constructs a new `MeleeSwingHandler` {@Code Object}. */ - : CombatSwingHandler(CombatStyle.MELEE) { + : CombatSwingHandler(CombatStyle.MELEE, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { //Credits wolfenzi, https://www.rune-server.ee/2009scape-development/rs2-server/snippets/608720-arios-hybridding-improve.html var distance = if (usingHalberd(entity)) 2 else 1 @@ -129,7 +129,8 @@ open class MeleeSwingHandler //formula taken from wiki: https://oldschool.runescape.wiki/w/Damage_per_second/Melee#Step_six:_Calculate_the_hit_chance Yes I know it's old school. It's the best resource we have for potentially authentic formulae. entity ?: return 0 var effectiveAttackLevel = entity.skills.getLevel(Skills.ATTACK).toDouble() - if(entity is Player) effectiveAttackLevel = floor(effectiveAttackLevel + (entity.prayer.getSkillBonus(Skills.ATTACK) * effectiveAttackLevel)) + if(entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveAttackLevel = floor(effectiveAttackLevel + (entity.prayer.getSkillBonus(Skills.ATTACK) * effectiveAttackLevel)) if(entity.properties.attackStyle.style == WeaponInterface.STYLE_ACCURATE) effectiveAttackLevel += 3 else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1 effectiveAttackLevel += 8 @@ -138,7 +139,10 @@ open class MeleeSwingHandler } effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK); effectiveAttackLevel = floor(effectiveAttackLevel) - effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) + + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) + else effectiveAttackLevel *= 64 val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" @@ -161,7 +165,7 @@ open class MeleeSwingHandler val level = entity!!.skills.getLevel(Skills.STRENGTH) var bonus = entity.properties.bonuses[11] var prayer = 1.0 - if (entity is Player) { + if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) { prayer += entity.prayer.getSkillBonus(Skills.STRENGTH) } var cumulativeStr = floor(level * prayer) @@ -175,6 +179,9 @@ open class MeleeSwingHandler if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT, entity) && getItemFromEquipment(entity, EquipmentSlot.WEAPON)?.definition?.getRequirement(Skills.STRENGTH) != 0) bonus = ceil(bonus * 1.20).toInt() + if (flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) + bonus = 0 + cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH) if(entity is Player && getSlayerTask(entity)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) diff --git a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt index b4871c192..50f54ce04 100644 --- a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt @@ -30,11 +30,11 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, conversion to Kotlin + cleanup */ -open class RangeSwingHandler +open class RangeSwingHandler (vararg flags: SwingHandlerFlag) /** * Constructs a new `RangeSwingHandler` {@Code Object}. */ - : CombatSwingHandler(CombatStyle.RANGE) { + : CombatSwingHandler(CombatStyle.RANGE, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { if (!isProjectileClipped(entity, victim, false)) { return InteractionType.NO_INTERACT @@ -184,14 +184,17 @@ open class RangeSwingHandler override fun calculateAccuracy(entity: Entity?): Int { entity ?: return 0 var effectiveRangedLevel = entity.skills.getLevel(Skills.RANGE).toDouble() - if(entity is Player) effectiveRangedLevel = floor(effectiveRangedLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveRangedLevel)) + if(entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveRangedLevel = floor(effectiveRangedLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveRangedLevel)) if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveRangedLevel += 3 effectiveRangedLevel += 8 effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE) if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1 effectiveRangedLevel = floor(effectiveRangedLevel) - effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) + else effectiveRangedLevel *= 64 return floor(effectiveRangedLevel).toInt() } @@ -200,7 +203,7 @@ open class RangeSwingHandler val level = entity!!.skills.getLevel(Skills.RANGE) val bonus = entity.properties.bonuses[14] var prayer = 1.0 - if (entity is Player) { + if (entity is Player && !flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE)) { prayer += entity.prayer.getSkillBonus(Skills.RANGE) } var cumulativeStr = floor(level * prayer) @@ -208,7 +211,11 @@ open class RangeSwingHandler cumulativeStr += 3.0 } cumulativeStr *= getSetMultiplier(entity, Skills.RANGE) - cumulativeStr *= (bonus + 64) + + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) + cumulativeStr *= (bonus + 64) + else cumulativeStr *= 64 + return floor((1.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt() //return ((14 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier).toInt() / 10 + 1 } diff --git a/Server/src/test/kotlin/content/CombatTests.kt b/Server/src/test/kotlin/content/CombatTests.kt new file mode 100644 index 000000000..55ffc715e --- /dev/null +++ b/Server/src/test/kotlin/content/CombatTests.kt @@ -0,0 +1,127 @@ +package content + +import TestUtils +import content.global.handlers.item.equipment.special.ChinchompaSwingHandler +import core.game.node.entity.combat.MagicSwingHandler +import core.game.node.entity.combat.MeleeSwingHandler +import core.game.node.entity.combat.RangeSwingHandler +import core.game.node.entity.combat.SwingHandlerFlag +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.player.link.prayer.PrayerType +import core.game.node.entity.skill.Skills +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class CombatTests { + init { + TestUtils.preTestSetup() + } + + @Test + fun swingHandlersFlaggedAsIgnoreStatsShouldNotInfluenceDamage() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE) + + TestUtils.getMockPlayer("ignorestatdamage").use { p -> + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateHit(p, p, 1.0) + p.properties.bonuses[14] = 250 + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateHit(p, p, 1.0)) + + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateHit(p, p, 1.0) + p.properties.bonuses[11] = 250 + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateHit(p, p, 1.0)) + } + } + + @Test + fun swingHandlersFlaggedAsIgnoreStatsShouldNotInfluenceAccuracy() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + val testMagicHandler = MagicSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + + TestUtils.getMockPlayer("ignorestataccuracy").use { p -> + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_RANGE) + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_RANGE] = 250 + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateAccuracy(p)) + + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_STAB) + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_STAB] = 250 + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateAccuracy(p)) + + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_MAGIC) + p.skills.setLevel(Skills.MAGIC, 99) + val baselineMagic = testMagicHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_MAGIC] = 250 + Assertions.assertEquals(baselineMagic, testMagicHandler.calculateAccuracy(p)) + } + } + + @Test + fun swingHandlersFlaggedAsIgnorePrayerShouldNotInfluenceDamage() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE) + + TestUtils.getMockPlayer("ignoreprayerdamage").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 1000.0 + + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateHit(p, p, 1.0) + p.prayer.toggle(PrayerType.EAGLE_EYE) + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateHit(p, p, 1.0)) + p.prayer.toggle(PrayerType.EAGLE_EYE) + + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateHit(p, p, 1.0) + p.prayer.toggle(PrayerType.SUPERHUMAN_STRENGTH) + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateHit(p, p, 1.0)) + p.prayer.toggle(PrayerType.SUPERHUMAN_STRENGTH) + } + } + + @Test + fun swingHandlersFlaggedAsIgnorePrayerShouldNotInfluenceAccuracy() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + val testMagicHandler = MagicSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + + TestUtils.getMockPlayer("ignoreprayeraccuracy").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 1000.0 + + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.EAGLE_EYE) + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.EAGLE_EYE) + + p.skills.setLevel(Skills.ATTACK, 99) + val baselineMelee = testMeleeHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.INCREDIBLE_REFLEXES) + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.INCREDIBLE_REFLEXES) + + p.skills.setLevel(Skills.MAGIC, 99) + val baselineMagic = testMagicHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.MYSTIC_MIGHT) + Assertions.assertEquals(baselineMagic, testMagicHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.MYSTIC_MIGHT) + } + } + + @Test fun chinchompaSwingHandlerIgnoresStatsForDamage() { + val handler = ChinchompaSwingHandler() + + TestUtils.getMockPlayer("chinchompaStatTest").use { p -> + val damageBaseline = handler.calculateHit(p, p, 1.0) + p.properties.bonuses[14] = 250 + Assertions.assertEquals(damageBaseline, handler.calculateHit(p, p, 1.0)) + } + } +} \ No newline at end of file