Unified dragonfire protection logic

Fixed KBD's attack range resulting in only certain spots allowing him to melee
This commit is contained in:
Ceikry
2023-04-18 08:44:03 +00:00
committed by Ryan
parent 4ded3b90de
commit cb92ad0577
5 changed files with 673 additions and 660 deletions
@@ -17,6 +17,8 @@ import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics;
import core.tools.RandomFunction;
import static core.api.ContentAPIKt.calculateDragonfireMaxHit;
/**
* Handles dragonfire combat.
* @author Emperor
@@ -100,7 +102,7 @@ public class DragonfireSwingHandler extends CombatSwingHandler {
@Override
public int swing(Entity entity, Entity victim, BattleState state) {
int max = calculateHit(entity, victim, 1.0);
int hit = RandomFunction.random(max);
int hit = RandomFunction.random(max + 1);
assert state != null;
state.setMaximumHit(max);
state.setStyle(CombatStyle.MAGIC);
@@ -125,7 +127,6 @@ public class DragonfireSwingHandler extends CombatSwingHandler {
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
if (entity instanceof NPC && victim instanceof Player) {
Player p = (Player) victim;
p.getPacketDispatch().sendMessage(getDragonfireMessage(victim.getAttribute("fire_resistance", 0), !fire ? "icy breath" : "fiery breath"));
Item shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD);
if (shield != null && (shield.getId() == 11283 || shield.getId() == 11284)) {
if (shield.getId() == 11284) {
@@ -192,20 +193,7 @@ public class DragonfireSwingHandler extends CombatSwingHandler {
@Override
public int calculateHit(Entity entity, Entity victim, double modifier) {
int damage = maximumHit;
if (victim instanceof Player) {
int val = victim.getDragonfireProtection(fire);
if ((val & 0x2) != 0) {
damage *= 0.1;
}
if ((val & 0x4) != 0) {
damage *= 0.1;
}
if ((val & 0x8) != 0) {
damage *= 0.6;
}
}
return damage;
return calculateDragonfireMaxHit(victim, maximumHit, !fire, 0, true);
}
@Override
@@ -31,19 +31,6 @@ class SkeletalWyvernBehavior : NPCBehavior(*Tasks.SKELETAL_WYVERN.ids) {
private val SHIELDS = intArrayOf(Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11285, Items.ELEMENTAL_SHIELD_2890, Items.MIND_SHIELD_9731)
override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) {
if (victim !is Player) return
if (state.style != CombatStyle.MAGIC) return
val shield = getItemFromEquipment(victim, EquipmentSlot.SHIELD)
val hasShieldProtection = shield != null && shield.id in SHIELDS
if (!hasShieldProtection) return
state.estimatedHit = RandomFunction.random(11)
if (victim.location.getDistance(self.location) >= 5 && victim.prayer.get(PrayerType.PROTECT_FROM_MAGIC))
state.estimatedHit = 0
}
override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler {
val victim = self.properties.combatPulse.getVictim() ?: return original
if (victim !is Player) return original
@@ -25,6 +25,8 @@ import core.plugin.Initializable;
import core.tools.RandomFunction;
import content.global.handlers.item.equipment.special.DragonfireSwingHandler;
import static core.api.ContentAPIKt.calculateDragonfireMaxHit;
/**
* Represents the Elvarg npc.
@@ -243,24 +245,8 @@ public final class ElvargNPC extends AbstractNPC {
if (style == CombatStyle.MELEE) {
return style.getSwingHandler().calculateHit(entity, victim, modifier);
}
int maxDamage = 60; // Max Possible hit without shield protection
int finalDamage = maxDamage;
if (victim instanceof Player) {
int val = victim.getDragonfireProtection(true); // DragonFireShield Protection
if ((val & 0x2) != 0) { // 0x2 - Anti Fire potion used?
finalDamage -= 0.22 * maxDamage; // = 20% protection
}
if ((val & 0x4) != 0) { // 0x4 - Anti Dragon Fire Shield equipped?
finalDamage -= 0.78 * maxDamage; // Should always equal 13 as that is the max hit possible with the shield equipped, 80% protection
}
if ( (val & 0x2) == 0 && (val & 0x4) == 0 && (val & 0x8) != 0) { // 0x8 - Magic Prayer Protection, should not stack with the others
finalDamage -= 0.6 * maxDamage; // = 36
}
}
return finalDamage;
return calculateDragonfireMaxHit(victim, 60, false, 3, true);
}
@Override
@@ -309,7 +295,7 @@ public final class ElvargNPC extends AbstractNPC {
if (isAccurateImpact(entity, victim)) {
int max = calculateHit(entity, victim, 1.0);
state.setMaximumHit(max);
hit = RandomFunction.random(max);
hit = RandomFunction.random(max + 1);
}
state.setEstimatedHit(hit);
return ticks;
@@ -20,6 +20,8 @@ import core.plugin.Initializable;
import core.plugin.Plugin;
import core.tools.RandomFunction;
import static core.api.ContentAPIKt.calculateDragonfireMaxHit;
/**
* Represents the King Black Dragon NPC.
* @author Emperor
@@ -155,27 +157,7 @@ public final class KingBlackDragonNPC extends AbstractNPC {
if (style == CombatStyle.MELEE) {
return style.getSwingHandler().calculateHit(entity, victim, modifier);
}
int max = 56;
int damage = max;
if (victim instanceof Player) {
int val = victim.getDragonfireProtection(true);
if ((val & 0x2) != 0) {
damage *= 0.5;
}
if ((val & 0x4) != 0) {
damage -= (int) (damage * 0.85);
}
if ((val & 0x8) != 0) {
damage *= 0.6;
}
if (damage < 3) {
damage = 3;
}
}
if (fireType != FireType.FIERY_BREATH) {
damage += 11;
}
return damage;
return calculateDragonfireMaxHit(victim, 56, false, fireType != FireType.FIERY_BREATH ? 10 : 0, true);
}
@Override
@@ -183,7 +165,7 @@ public final class KingBlackDragonNPC extends AbstractNPC {
if (!isProjectileClipped(entity, victim, false)) {
return InteractionType.NO_INTERACT;
}
if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) == InteractionType.STILL_INTERACT) {
if (victim.getCenterLocation().withinMaxnormDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) == InteractionType.STILL_INTERACT) {
entity.getWalkingQueue().reset();
return InteractionType.STILL_INTERACT;
}
@@ -210,7 +192,7 @@ public final class KingBlackDragonNPC extends AbstractNPC {
style = CombatStyle.RANGE;
int hit = 0;
int ticks = 1;
if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) && RandomFunction.random(10) < 7) {
if (victim.getCenterLocation().withinMaxnormDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) && RandomFunction.random(10) < 7) {
style = CombatStyle.MELEE;
} else {
ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3);
@@ -220,7 +202,7 @@ public final class KingBlackDragonNPC extends AbstractNPC {
if (isAccurateImpact(entity, victim)) {
int max = calculateHit(entity, victim, 1.0);
state.setMaximumHit(max);
hit = RandomFunction.random(max);
hit = RandomFunction.random(max + 1);
}
state.setEstimatedHit(hit);
((NPC) entity).getAggressiveHandler().setPauseTicks(2);
+70
View File
@@ -20,6 +20,7 @@ import core.game.node.entity.player.link.TeleportManager
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.player.link.emote.Emotes
import core.game.node.entity.player.link.quest.QuestRepository
import core.game.node.entity.player.link.prayer.PrayerType;
import core.game.node.entity.skill.Skills
import content.data.skill.SkillingTool
import content.global.skill.slayer.Tasks
@@ -2462,4 +2463,73 @@ fun decantContainer (container: core.game.container.Container) : Pair<List<Item>
return Pair<List<Item>,List<Item>>(toRemove, toAdd)
}
/**
* Checks whether the user has a valid anti-dragonfire shield equipped.
* @param player the player whose shield we are checking.
* @param wyvern an optional parameter (default false) whether or not we are checking against wyvern fire, which ignores normal anti-dragon-shields, but accepts elemental/mind shields, etc.
* @return whether or not the player is protected by their shield.
* @see <a href=https://runescape.wiki/w/Dragonfire?oldid=2068032>Source</a>
**/
fun hasDragonfireShieldProtection(player: Player, wyvern: Boolean = false): Boolean {
val shield = getItemFromEquipment(player, EquipmentSlot.SHIELD) ?: return false
return when (shield.id) {
Items.ELEMENTAL_SHIELD_2890, Items.MIND_SHIELD_9731 -> wyvern
Items.ANTI_DRAGON_SHIELD_1540 -> !wyvern
Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11284 -> true
else -> false
}
}
/**
* Calculates the expected max hit of dragonfire after checking/applying valid protections.
* @param entity the entity whose protection we are checking.
* @param maxDamage the max damage the dragonfire can do without protection.
* @param wyvern an optional parameter (default false) that defines whether or not this is wyvern fire. Wyvern fire ignores anti-fire potions and regular anti-dragon shield.
* @param unprotectableDamage an optional parameter (default 0) that defines how much damage cannot be protected against. Think of it as a minimum-max-hit.
* @param sendMessage an optional parameter (default false) whether or not to send a message notifying the player of the damage being blocked.
* @return the maximum amount of damage that can be done to the player.
* @see <a href=https://runescape.wiki/w/Dragonfire?oldid=2068032>Source</a>
**/
fun calculateDragonfireMaxHit(entity: Entity, maxDamage: Int, wyvern: Boolean = false, unprotectableDamage: Int = 0, sendMessage: Boolean = false): Int {
val hasShield: Boolean
val hasPotion: Boolean
val hasPrayer: Boolean
if (entity is Player) {
hasShield = hasDragonfireShieldProtection(entity, wyvern)
hasPotion = !wyvern && getAttribute(entity, "fire:immune", 0) >= getWorldTicks()
hasPrayer = entity.prayer.get(PrayerType.PROTECT_FROM_MAGIC)
if (sendMessage) {
var message = "You are horribly burnt by the ${if (wyvern) "icy" else "fiery"} breath."
if (hasShield && hasPotion)
message = "Your potion and shield fully absorb the ${if (wyvern) "icy" else "fiery"} breath."
else if (hasShield)
message = "Your shield absorbs most of the ${if (wyvern) "icy" else "fiery"} breath."
else if (hasPotion)
message = "Your potion absorbs some of the fiery breath."
else if (hasPrayer)
message = "Your prayer absorbs some of the ${if (wyvern) "icy" else "fiery"} breath."
sendMessage(entity, message)
}
} else {
val dragonfireTokens = entity.getDragonfireProtection(!wyvern)
hasShield = dragonfireTokens and 0x2 != 0
hasPotion = dragonfireTokens and 0x4 != 0
hasPrayer = dragonfireTokens and 0x8 != 0
}
var effectiveDamage = maxDamage.toDouble()
if (hasPrayer && !hasShield && !hasPotion)
effectiveDamage -= 0.6 * maxDamage
else {
if (hasShield)
effectiveDamage -= 0.8 * maxDamage
if (hasPotion)
effectiveDamage -= 0.2 * maxDamage
}
return Math.max(unprotectableDamage, effectiveDamage.toInt())
}
private class ContentAPI