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 82bf1d5d8..0062c20d9 100644 --- a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt @@ -1,6 +1,7 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.summoning.SummoningPouch import core.game.node.entity.Entity import core.game.node.entity.combat.equipment.ArmourSet import core.game.node.entity.combat.spell.SpellType @@ -200,6 +201,7 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) effectiveMagicLevel = floor(effectiveMagicLevel + (victim.prayer.getSkillBonus(Skills.MAGIC) * effectiveMagicLevel)) effectiveDefenceLevel = effectiveDefenceLevel * 0.3 + effectiveMagicLevel * 0.7 + 8 + effectiveDefenceLevel *= familiarDefenceBonus(victim) return effectiveDefenceLevel.toInt() * styleDefenceBonus } is NPC -> { @@ -220,6 +222,20 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag) return 1.0 } + /** + * Check to see whether the player is receiving a defensive bonus from an active familiar. + * - Wolpertinger => +5% + */ + private fun familiarDefenceBonus(e: Entity?): Double { + if (e !is Player) return 1.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 1.0 + return if (fam.pouchId == SummoningPouch.WOLPERTINGER_POUCH.pouchId) 1.05 else 1.0 + } + override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { if (state?.spell != null && entity is Player) { state.spell.addExperience(entity, state.totalDamage) 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 d745abe83..91864ec53 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -4,6 +4,7 @@ import content.global.skill.skillcapeperks.SkillcapePerks import content.global.skill.slayer.SlayerEquipmentFlags import content.global.skill.slayer.SlayerManager import content.global.skill.slayer.Tasks +import content.global.skill.summoning.SummoningPouch import core.api.* import core.api.EquipmentSlot import core.game.container.impl.EquipmentContainer @@ -235,6 +236,7 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) else if (victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefenceLevel += 1 effectiveDefenceLevel += 8 effectiveDefenceLevel *= getSetMultiplier(victim, Skills.DEFENCE) + effectiveDefenceLevel *= familiarDefenceBonus(victim) return effectiveDefenceLevel.toInt() * styleDefenceBonus } is NPC -> { @@ -282,6 +284,25 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) || name == "Skeleton" || name == "Zogre" || name == "Spiritual Mage") } + /** + * Check to see whether the player is receiving a defensive bonus from an active familiar. + * - Iron Titan => +10% + * - Steel Titan => +15% + */ + private fun familiarDefenceBonus(e: Entity?): Double { + if (e !is Player) return 1.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 1.0 + return when (fam.pouchId) { + SummoningPouch.IRON_TITAN_POUCH.pouchId -> 1.10 + SummoningPouch.STEEL_TITAN_POUCH.pouchId -> 1.15 + else -> 1.0 + } + } + companion object { /** * Checks if the entity is using a halberd. 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 335a32abc..a5b313a8b 100644 --- a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt @@ -1,6 +1,7 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.summoning.SummoningPouch import core.api.* import core.game.container.impl.EquipmentContainer import core.game.node.entity.Entity @@ -207,6 +208,7 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandl effectiveRangedLevel = floor(effectiveRangedLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveRangedLevel)) if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveRangedLevel += 3 effectiveRangedLevel += 8 + effectiveRangedLevel += familiarOffenceBonus(entity) effectiveRangedLevel *= getSetMultiplier(entity, Skills.RANGE) if(SkillcapePerks.isActive(SkillcapePerks.ACCURATE_MARKSMAN,entity)) effectiveRangedLevel *= 1.1 @@ -245,6 +247,7 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandl effectiveStrengthLevel = floor(effectiveStrengthLevel + (entity.prayer.getSkillBonus(Skills.RANGE) * effectiveStrengthLevel)) if(entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) effectiveStrengthLevel += 3 effectiveStrengthLevel += 8 + effectiveStrengthLevel += familiarOffenceBonus(entity) effectiveStrengthLevel *= getSetMultiplier(entity, Skills.RANGE) effectiveStrengthLevel = floor(effectiveStrengthLevel) if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE)) @@ -301,6 +304,22 @@ open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandl } else super.getArmourSet(e) } + /** + * Check to see whether the player is receiving an offensive bonus from an active familiar. + * - Geyser Titan => 1 + 3% of ranged level + */ + private fun familiarOffenceBonus(e: Entity?): Double { + if (e !is Player) return 0.0 + val fam = try { + e.familiarManager?.familiar + } catch (ex: Exception) { + null + } ?: return 0.0 + return if (fam.pouchId == SummoningPouch.GEYSER_TITAN_POUCH.pouchId) { + 1.0 + ceil(e.skills.getLevel(Skills.RANGE) * 0.03) + } else 0.0 + } + companion object { /** * Checks if the entity has the ammunition needed to proceed. diff --git a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt index a618f3ca1..51023b308 100644 --- a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -27,6 +27,7 @@ import core.game.system.communication.CommunicationInfo import core.game.world.map.RegionManager import core.game.world.map.build.DynamicRegion import core.game.world.repository.Repository +import core.game.node.entity.combat.equipment.WeaponInterface import core.plugin.Initializable import core.tools.Log import core.tools.StringUtils @@ -131,6 +132,58 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player, "max hit: ${hit} (${(swingHandler as Object).getClass().getName()})") } + define("calcdefence", Privilege.STANDARD, "::calcdefence NPC ID", "Calculates and prints your current raw defence values for Melee, Ranged and Magic against a given NPC.") { player, args -> + val meleeHandler = core.game.node.entity.combat.MeleeSwingHandler() + val rangeHandler = core.game.node.entity.combat.RangeSwingHandler() + val magicHandler = core.game.node.entity.combat.MagicSwingHandler() + var attacker: core.game.node.entity.Entity = player + if (args.size > 1) { + val npcId = args[1].toInt() + val npc = NPC(npcId) + npc.initConfig() + attacker = npc + notify(player, "Calculating defence against NPC: ${npc.name} (ID=$npcId)") + val bonuses = npc.properties.bonuses + var maxBonus = Int.MIN_VALUE + var maxIndex = 0 + for (i in 0..4) { + if (i in bonuses.indices) { + val v = bonuses[i] + if (v > maxBonus) { + maxBonus = v + maxIndex = i + } + } + } + if (maxBonus == Int.MIN_VALUE) { + maxBonus = 0 + maxIndex = 0 + } + npc.properties.attackStyle = WeaponInterface.AttackStyle(WeaponInterface.STYLE_CONTROLLED,maxIndex) + } else { + notify(player, "Calculating defence against yourself (no NPC specified).") + } + fun hitChance(accuracy: Int, defence: Int): Double { + return if (accuracy > defence) { + 1.0 - ((defence + 2.0) / (2.0 * (accuracy + 1.0))) + } else { + accuracy / (2.0 * (defence + 1.0)) + } + } + val meleeAccuracy = meleeHandler.calculateAccuracy(attacker) + val meleeDefence = meleeHandler.calculateDefence(player, attacker) + val meleeChance = hitChance(meleeAccuracy, meleeDefence) + notify(player, "Your melee defence: $meleeDefence; Attacker accuracy: $meleeAccuracy; Chance to be hit: $meleeChance") + val rangeAccuracy = rangeHandler.calculateAccuracy(attacker) + val rangeDefence = rangeHandler.calculateDefence(player, attacker) + val rangeChance = hitChance(rangeAccuracy, rangeDefence) + notify(player, "Your range defence: $rangeDefence; Attacker accuracy: $rangeAccuracy; Chance to be hit: $rangeChance") + val magicAccuracy = magicHandler.calculateAccuracy(attacker) + val magicDefence = magicHandler.calculateDefence(player, attacker) + val magicChance = hitChance(magicAccuracy, magicDefence) + notify(player, "Your magic defence: $magicDefence; Attacker accuracy: $magicAccuracy; Chance to be hit: $magicChance") + } + /** * Empty a player's inventory * ADMIN only (for obvious reasons)