Swing handler flags to ignore modifiers + fix issue with chins

This commit is contained in:
ceikry 2023-07-30 16:40:40 -05:00
parent 9b163cea32
commit 691220c8d1
6 changed files with 170 additions and 22 deletions

View file

@ -1,9 +1,7 @@
package content.global.handlers.item.equipment.special; package content.global.handlers.item.equipment.special;
import core.game.node.entity.Entity; import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.*;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.InteractionType;
import core.game.node.entity.combat.equipment.Ammunition; import core.game.node.entity.combat.equipment.Ammunition;
import core.game.node.entity.combat.equipment.RangeWeapon; import core.game.node.entity.combat.equipment.RangeWeapon;
import core.game.node.entity.combat.equipment.Weapon; 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.map.RegionManager;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.tools.RandomFunction; import core.tools.RandomFunction;
import core.game.node.entity.combat.RangeSwingHandler;
import java.util.List; import java.util.List;
@ -37,8 +34,8 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
/** /**
* Constructs a new {@code ChinchompaSwingHandler} {@code Object}. * Constructs a new {@code ChinchompaSwingHandler} {@code Object}.
*/ */
private ChinchompaSwingHandler() { public ChinchompaSwingHandler() {
super(); super(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE);
} }
@Override @Override

View file

@ -31,7 +31,7 @@ import kotlin.math.floor
* @author Emperor * @author Emperor
* @author Ceikry - Kotlin refactoring, general cleanup * @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. * 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
}

View file

@ -15,8 +15,8 @@ import kotlin.math.floor
* @author Emperor * @author Emperor
* @author Ceikry, Kotlin conversion + cleanup * @author Ceikry, Kotlin conversion + cleanup
*/ */
open class MagicSwingHandler open class MagicSwingHandler (vararg flags: SwingHandlerFlag)
: CombatSwingHandler(CombatStyle.MAGIC) { : CombatSwingHandler(CombatStyle.MAGIC, *flags) {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? { override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
if (!isProjectileClipped(entity, victim, false)) { if (!isProjectileClipped(entity, victim, false)) {
@ -151,12 +151,15 @@ open class MagicSwingHandler
} }
val level = entity.skills.getLevel(Skills.MAGIC, true) val level = entity.skills.getLevel(Skills.MAGIC, true)
var prayer = 1.0 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) prayer += entity.prayer.getSkillBonus(Skills.MAGIC)
} }
val additional = getSetMultiplier(entity, Skills.MAGIC); val additional = getSetMultiplier(entity, Skills.MAGIC);
val effective = floor(level * prayer * additional + spellBonus) 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() return floor((effective + 8) * (bonus + 64) / 10).toInt()
} }

View file

@ -24,11 +24,11 @@ import kotlin.math.floor
* @author Emperor * @author Emperor
* @author Ceikry, Kotlin conversion + cleanup * @author Ceikry, Kotlin conversion + cleanup
*/ */
open class MeleeSwingHandler open class MeleeSwingHandler (vararg flags: SwingHandlerFlag)
/** /**
* Constructs a new `MeleeSwingHandler` {@Code Object}. * Constructs a new `MeleeSwingHandler` {@Code Object}.
*/ */
: CombatSwingHandler(CombatStyle.MELEE) { : CombatSwingHandler(CombatStyle.MELEE, *flags) {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? { 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 //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 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. //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 entity ?: return 0
var effectiveAttackLevel = entity.skills.getLevel(Skills.ATTACK).toDouble() 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 if(entity.properties.attackStyle.style == WeaponInterface.STYLE_ACCURATE) effectiveAttackLevel += 3
else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1 else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1
effectiveAttackLevel += 8 effectiveAttackLevel += 8
@ -138,7 +139,10 @@ open class MeleeSwingHandler
} }
effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK); effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK);
effectiveAttackLevel = floor(effectiveAttackLevel) effectiveAttackLevel = floor(effectiveAttackLevel)
if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY))
effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64)
else effectiveAttackLevel *= 64
val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none"
@ -161,7 +165,7 @@ open class MeleeSwingHandler
val level = entity!!.skills.getLevel(Skills.STRENGTH) val level = entity!!.skills.getLevel(Skills.STRENGTH)
var bonus = entity.properties.bonuses[11] var bonus = entity.properties.bonuses[11]
var prayer = 1.0 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) prayer += entity.prayer.getSkillBonus(Skills.STRENGTH)
} }
var cumulativeStr = floor(level * prayer) 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) 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() bonus = ceil(bonus * 1.20).toInt()
if (flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE))
bonus = 0
cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH) cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH)
if(entity is Player && getSlayerTask(entity)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) if(entity is Player && getSlayerTask(entity)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true)

View file

@ -30,11 +30,11 @@ import kotlin.math.floor
* @author Emperor * @author Emperor
* @author Ceikry, conversion to Kotlin + cleanup * @author Ceikry, conversion to Kotlin + cleanup
*/ */
open class RangeSwingHandler open class RangeSwingHandler (vararg flags: SwingHandlerFlag)
/** /**
* Constructs a new `RangeSwingHandler` {@Code Object}. * Constructs a new `RangeSwingHandler` {@Code Object}.
*/ */
: CombatSwingHandler(CombatStyle.RANGE) { : CombatSwingHandler(CombatStyle.RANGE, *flags) {
override fun canSwing(entity: Entity, victim: Entity): InteractionType? { override fun canSwing(entity: Entity, victim: Entity): InteractionType? {
if (!isProjectileClipped(entity, victim, false)) { if (!isProjectileClipped(entity, victim, false)) {
return InteractionType.NO_INTERACT return InteractionType.NO_INTERACT
@ -184,14 +184,17 @@ open class RangeSwingHandler
override fun calculateAccuracy(entity: Entity?): Int { override fun calculateAccuracy(entity: Entity?): Int {
entity ?: return 0 entity ?: return 0
var effectiveRangedLevel = entity.skills.getLevel(Skills.RANGE).toDouble() 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 if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveRangedLevel += 3
effectiveRangedLevel += 8 effectiveRangedLevel += 8
effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE) effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE)
if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1 if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1
effectiveRangedLevel = floor(effectiveRangedLevel) effectiveRangedLevel = floor(effectiveRangedLevel)
if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY))
effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64)
else effectiveRangedLevel *= 64
return floor(effectiveRangedLevel).toInt() return floor(effectiveRangedLevel).toInt()
} }
@ -200,7 +203,7 @@ open class RangeSwingHandler
val level = entity!!.skills.getLevel(Skills.RANGE) val level = entity!!.skills.getLevel(Skills.RANGE)
val bonus = entity.properties.bonuses[14] val bonus = entity.properties.bonuses[14]
var prayer = 1.0 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) prayer += entity.prayer.getSkillBonus(Skills.RANGE)
} }
var cumulativeStr = floor(level * prayer) var cumulativeStr = floor(level * prayer)
@ -208,7 +211,11 @@ open class RangeSwingHandler
cumulativeStr += 3.0 cumulativeStr += 3.0
} }
cumulativeStr *= getSetMultiplier(entity, Skills.RANGE) cumulativeStr *= getSetMultiplier(entity, Skills.RANGE)
if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE))
cumulativeStr *= (bonus + 64) cumulativeStr *= (bonus + 64)
else cumulativeStr *= 64
return floor((1.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt() return floor((1.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt()
//return ((14 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier).toInt() / 10 + 1 //return ((14 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier).toInt() / 10 + 1
} }

View file

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