From cb92ad05776cce366a4db1d853aa96240526e788 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Tue, 18 Apr 2023 08:44:03 +0000 Subject: [PATCH] Unified dragonfire protection logic Fixed KBD's attack range resulting in only certain spots allowing him to melee --- .../special/DragonfireSwingHandler.java | 342 ++++++------ .../skill/slayer/SkeletalWyvernBehavior.kt | 15 +- .../varrock/quest/dragonslayer/ElvargNPC.java | 512 +++++++++--------- .../handlers/KingBlackDragonNPC.java | 394 +++++++------- Server/src/main/core/api/ContentAPI.kt | 70 +++ 5 files changed, 673 insertions(+), 660 deletions(-) diff --git a/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java index 70bb5fdcc..68f32b8a9 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java @@ -17,205 +17,193 @@ 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 */ public class DragonfireSwingHandler extends CombatSwingHandler { - /** - * If the NPC has to be in melee range. - */ - private boolean meleeRange; + /** + * If the NPC has to be in melee range. + */ + private boolean meleeRange; - /** - * The maximum hit. - */ - private int maximumHit; + /** + * The maximum hit. + */ + private int maximumHit; - /** - * The attack data. - */ - private SwitchAttack attack; + /** + * The attack data. + */ + private SwitchAttack attack; - /** - * IF the dragon attack is firey or icey. - */ - private boolean fire; + /** + * IF the dragon attack is firey or icey. + */ + private boolean fire; - /** - * Constructs a new {@code DragonfireSwingHandler} {@code Object}. - * @param meleeRange If the NPC has to be in melee range. - * @param maximumHit The maximum hit. - * @param fire if firey. - */ - public DragonfireSwingHandler(boolean meleeRange, int maximumHit, SwitchAttack attack, boolean fire) { - super(CombatStyle.MAGIC); - this.meleeRange = meleeRange; - this.maximumHit = maximumHit; - this.attack = attack; - this.fire = fire; - } + /** + * Constructs a new {@code DragonfireSwingHandler} {@code Object}. + * @param meleeRange If the NPC has to be in melee range. + * @param maximumHit The maximum hit. + * @param fire if firey. + */ + public DragonfireSwingHandler(boolean meleeRange, int maximumHit, SwitchAttack attack, boolean fire) { + super(CombatStyle.MAGIC); + this.meleeRange = meleeRange; + this.maximumHit = maximumHit; + this.attack = attack; + this.fire = fire; + } - /** - * Gets the switch attack instance for a dragonfire attack. - * @param meleeRange If the attack is melee range. - * @param maximumHit The maximum hit. - * @param animation The animation. - * @param startGraphic The start graphic. - * @param endGraphic The end graphic. - * @param projectile The projectile. - * @return The switch attack instance. - */ - public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { - SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); - attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, true)); - return attack; - } + /** + * Gets the switch attack instance for a dragonfire attack. + * @param meleeRange If the attack is melee range. + * @param maximumHit The maximum hit. + * @param animation The animation. + * @param startGraphic The start graphic. + * @param endGraphic The end graphic. + * @param projectile The projectile. + * @return The switch attack instance. + */ + public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { + SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); + attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, true)); + return attack; + } - /** - * Gets the switch attack instance for a dragonfire attack. - * @param meleeRange If the attack is melee range. - * @param maximumHit The maximum hit. - * @param animation The animation. - * @param startGraphic The start graphic. - * @param endGraphic The end graphic. - * @param projectile The projectile. - * @return The switch attack instance. - */ - public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile, boolean fire) { - SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); - attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, fire)); - return attack; - } + /** + * Gets the switch attack instance for a dragonfire attack. + * @param meleeRange If the attack is melee range. + * @param maximumHit The maximum hit. + * @param animation The animation. + * @param startGraphic The start graphic. + * @param endGraphic The end graphic. + * @param projectile The projectile. + * @return The switch attack instance. + */ + public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile, boolean fire) { + SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); + attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, fire)); + return attack; + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - if (meleeRange) { - return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim); - } - return CombatStyle.MAGIC.getSwingHandler().canSwing(entity, victim); - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (meleeRange) { + return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim); + } + return CombatStyle.MAGIC.getSwingHandler().canSwing(entity, victim); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - int max = calculateHit(entity, victim, 1.0); - int hit = RandomFunction.random(max); - assert state != null; - state.setMaximumHit(max); - state.setStyle(CombatStyle.MAGIC); - state.setEstimatedHit(hit); - if (meleeRange) { - return 1; - } - int ticks = 2 + (int) Math.floor(entity.getLocation().getDistance(victim.getLocation()) * 0.5); - entity.setAttribute("fireBreath", GameWorld.getTicks() + (ticks + 2)); - return ticks; - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + int max = calculateHit(entity, victim, 1.0); + int hit = RandomFunction.random(max + 1); + assert state != null; + state.setMaximumHit(max); + state.setStyle(CombatStyle.MAGIC); + state.setEstimatedHit(hit); + if (meleeRange) { + return 1; + } + int ticks = 2 + (int) Math.floor(entity.getLocation().getDistance(victim.getLocation()) * 0.5); + entity.setAttribute("fireBreath", GameWorld.getTicks() + (ticks + 2)); + return ticks; + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(attack.getAnimation(), attack.getStartGraphic()); - if (attack.getProjectile() != null) { - attack.getProjectile().copy(entity, victim, 5).send(); - } - } + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + entity.visualize(attack.getAnimation(), attack.getStartGraphic()); + if (attack.getProjectile() != null) { + attack.getProjectile().copy(entity, victim, 5).send(); + } + } - @Override - 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) { - p.getEquipment().replace(new Item(11283), EquipmentContainer.SLOT_SHIELD); - shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - shield.setCharge(0); - } - if (shield.getCharge() < 1000) { - shield.setCharge(shield.getCharge() + 20); - EquipmentContainer.updateBonuses(p); - p.getPacketDispatch().sendMessage("Your dragonfire shield glows more brightly."); + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (entity instanceof NPC && victim instanceof Player) { + Player p = (Player) victim; + Item shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); + if (shield != null && (shield.getId() == 11283 || shield.getId() == 11284)) { + if (shield.getId() == 11284) { + p.getEquipment().replace(new Item(11283), EquipmentContainer.SLOT_SHIELD); + shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); + shield.setCharge(0); + } + if (shield.getCharge() < 1000) { + shield.setCharge(shield.getCharge() + 20); + EquipmentContainer.updateBonuses(p); + p.getPacketDispatch().sendMessage("Your dragonfire shield glows more brightly."); - p.faceLocation(entity.getCenterLocation()); - victim.visualize(Animation.create(6695), Graphics.create(1163)); - } else { - p.getPacketDispatch().sendMessage("Your dragonfire shield is already fully charged."); - } - return; - } - } - if (!fire && victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks() && RandomFunction.random(4) == 2) { - victim.getStateManager().set(EntityState.FROZEN, 16); - victim.graphics(Graphics.create(502)); - } - Graphics graphic = attack != null ? attack.getEndGraphic() : null; - victim.visualize(victim.getProperties().getDefenceAnimation(), graphic); - } + p.faceLocation(entity.getCenterLocation()); + victim.visualize(Animation.create(6695), Graphics.create(1163)); + } else { + p.getPacketDispatch().sendMessage("Your dragonfire shield is already fully charged."); + } + return; + } + } + if (!fire && victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks() && RandomFunction.random(4) == 2) { + victim.getStateManager().set(EntityState.FROZEN, 16); + victim.graphics(Graphics.create(502)); + } + Graphics graphic = attack != null ? attack.getEndGraphic() : null; + victim.visualize(victim.getProperties().getDefenceAnimation(), graphic); + } - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - assert state != null; - int hit = state.getEstimatedHit(); - if (hit > -1) { - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); - } - hit = state.getSecondaryHit(); - if (hit > -1) { - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); - } - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + assert state != null; + int hit = state.getEstimatedHit(); + if (hit > -1) { + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); + } + hit = state.getSecondaryHit(); + if (hit > -1) { + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); + } + } - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (victim.isPlayer() && !fire) { - Item item = victim.asPlayer().getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if (item != null && (item.getId() == 2890 || item.getId() == 9731) && state.getEstimatedHit() > 10) { - state.setEstimatedHit(RandomFunction.random(10)); - } - } - CombatStyle style = state.getStyle(); - super.adjustBattleState(entity, victim, state); - state.setStyle(style); - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (victim.isPlayer() && !fire) { + Item item = victim.asPlayer().getEquipment().get(EquipmentContainer.SLOT_SHIELD); + if (item != null && (item.getId() == 2890 || item.getId() == 9731) && state.getEstimatedHit() > 10) { + state.setEstimatedHit(RandomFunction.random(10)); + } + } + CombatStyle style = state.getStyle(); + super.adjustBattleState(entity, victim, state); + state.setStyle(style); + } - @Override - protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { - return formatHit(victim, hit); - } + @Override + protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { + return formatHit(victim, hit); + } - @Override - public int calculateAccuracy(Entity entity) { - return 4000; - } + @Override + public int calculateAccuracy(Entity entity) { + return 4000; + } - @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; - } + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + return calculateDragonfireMaxHit(victim, maximumHit, !fire, 0, true); + } - @Override - public int calculateDefence(Entity victim, Entity attacker) { - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return 1.0; - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return 1.0; + } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt b/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt index 64b3f4e37..4636cd51b 100644 --- a/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt +++ b/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt @@ -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 @@ -53,4 +40,4 @@ class SkeletalWyvernBehavior : NPCBehavior(*Tasks.SKELETAL_WYVERN.ids) { else COMBAT_HANDLER } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java index a112cfcf3..047821ec9 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java @@ -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. @@ -34,311 +36,295 @@ import content.global.handlers.item.equipment.special.DragonfireSwingHandler; @Initializable public final class ElvargNPC extends AbstractNPC { - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 742 }; + /** + * The NPC ids of NPCs using this plugin. + */ + private static final int[] ID = { 742 }; - /** - * Represents the animations to use. - */ - private static final Animation[] ANIMATIONS = new Animation[] { new Animation(6654), new Animation(6655) }; + /** + * Represents the animations to use. + */ + private static final Animation[] ANIMATIONS = new Animation[] { new Animation(6654), new Animation(6655) }; - /** - * Represents the combat swing handler. - */ - private final CombatSwingHandler combatHandler = new ElvargCombatSwingHandler(); + /** + * Represents the combat swing handler. + */ + private final CombatSwingHandler combatHandler = new ElvargCombatSwingHandler(); - /** - * Constructs a new {@code ElvargNPC} {@code Object}. - */ - public ElvargNPC() { - super(0, null); - } + /** + * Constructs a new {@code ElvargNPC} {@code Object}. + */ + public ElvargNPC() { + super(0, null); + } - /** - * Constructs a new {@code ElvargNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private ElvargNPC(int id, Location location) { - super(id, location); - } + /** + * Constructs a new {@code ElvargNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private ElvargNPC(int id, Location location) { + super(id, location); + } - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ElvargNPC(id, location); - } + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new ElvargNPC(id, location); + } - @Override - public void commenceDeath(Entity killer) { - final Direction direction = Direction.getLogicalDirection(getLocation(), killer.getLocation()); - GameWorld.getPulser().submit(new Pulse(1, this) { - @Override - public boolean pulse() { - faceLocation(getCenterLocation().transform(direction.getStepX() * 3, direction.getStepY() * 3, 0)); - return true; - } - }); - setDirection(direction); - } + @Override + public void commenceDeath(Entity killer) { + final Direction direction = Direction.getLogicalDirection(getLocation(), killer.getLocation()); + GameWorld.getPulser().submit(new Pulse(1, this) { + @Override + public boolean pulse() { + faceLocation(getCenterLocation().transform(direction.getStepX() * 3, direction.getStepY() * 3, 0)); + return true; + } + }); + setDirection(direction); + } - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - final Scenery object = new Scenery(25202, getLocation(), getRotation()); - SceneryBuilder.add(object); - killer.faceLocation(object.getCenterLocation()); - killer.lock(); - GameWorld.getPulser().submit(new Pulse(1) { - int counter = 0; + @Override + public void finalizeDeath(final Entity killer) { + super.finalizeDeath(killer); + final Scenery object = new Scenery(25202, getLocation(), getRotation()); + SceneryBuilder.add(object); + killer.faceLocation(object.getCenterLocation()); + killer.lock(); + GameWorld.getPulser().submit(new Pulse(1) { + int counter = 0; - @Override - public boolean pulse() { - counter++; - if (counter == 1) { - killer.animate(ANIMATIONS[0]); - } else if (counter == 4) { - final Player player = ((Player) killer); - SceneryBuilder.replace(object, object.transform(25203)); - if (!player.getInventory().add(DragonSlayer.ELVARG_HEAD)) { - GroundItemManager.create(DragonSlayer.ELVARG_HEAD, player); - } - killer.animate(ANIMATIONS[1]); - killer.unlock(); - } else if (counter == 12) { - SceneryBuilder.remove(RegionManager.getObject(object.getLocation())); - return true; - } - return false; - } + @Override + public boolean pulse() { + counter++; + if (counter == 1) { + killer.animate(ANIMATIONS[0]); + } else if (counter == 4) { + final Player player = ((Player) killer); + SceneryBuilder.replace(object, object.transform(25203)); + if (!player.getInventory().add(DragonSlayer.ELVARG_HEAD)) { + GroundItemManager.create(DragonSlayer.ELVARG_HEAD, player); + } + killer.animate(ANIMATIONS[1]); + killer.unlock(); + } else if (counter == 12) { + SceneryBuilder.remove(RegionManager.getObject(object.getLocation())); + return true; + } + return false; + } - }); - } + }); + } - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatHandler; - } + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatHandler; + } - @Override - public int[] getIds() { - return ID; - } + @Override + public int[] getIds() { + return ID; + } - /** - * Method used to get the rotation. - * @return the rotation. - */ - public int getRotation() { - switch (getDirection()) { - case EAST: - return 3; - case NORTH: - return 2; - case WEST: - return 1; - case SOUTH: - return 0; - default: - break; - } - return 0; - } + /** + * Method used to get the rotation. + * @return the rotation. + */ + public int getRotation() { + switch (getDirection()) { + case EAST: + return 3; + case NORTH: + return 2; + case WEST: + return 1; + case SOUTH: + return 0; + default: + break; + } + return 0; + } - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if (!(entity instanceof Player)) { - return super.isAttackable(entity, style, message); - } - final Player player = (Player) entity; - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (!(entity instanceof Player)) { + return super.isAttackable(entity, style, message); + } + final Player player = (Player) entity; + if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) == 40 && (player.getInventory().containsItem(DragonSlayer.ELVARG_HEAD))) { if(message) { player.getPacketDispatch().sendMessage("You have already slain the dragon. Now you just need to return to Oziach for"); player.getPacketDispatch().sendMessage("your reward!"); } - return false; - } - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) > 40) { + return false; + } + if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) > 40) { if(message) { player.getPacketDispatch().sendMessage("You have already slain Elvarg."); } - return false; - } - return super.isAttackable(entity, style, message); - } + return false; + } + return super.isAttackable(entity, style, message); + } - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } - /** - * Handles the Elvarg combat swing handler. - * @author Emperor - */ - static class ElvargCombatSwingHandler extends CombatSwingHandler { + /** + * Handles the Elvarg combat swing handler. + * @author Emperor + */ + static class ElvargCombatSwingHandler extends CombatSwingHandler { - /** - * The style. - */ - private CombatStyle style = CombatStyle.RANGE; + /** + * The style. + */ + private CombatStyle style = CombatStyle.RANGE; - /** - * The melee attack animation. - */ - private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); + /** + * The melee attack animation. + */ + private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - /** - * The fire type. - */ - private final FireType fireType = FireType.FIERY_BREATH; + /** + * The fire type. + */ + private final FireType fireType = FireType.FIERY_BREATH; - /** - * The DragonFire Attack */ - private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 60, null, true); + /** + * The DragonFire Attack */ + private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 60, null, true); - /** - * Constructs a new {@Code ElvargCombatSwingHandler} {@Code CombatStyle}. - * The Combat style. - */ - public ElvargCombatSwingHandler() { - super(CombatStyle.RANGE); - } + /** + * Constructs a new {@Code ElvargCombatSwingHandler} {@Code CombatStyle}. + * The Combat style. + */ + public ElvargCombatSwingHandler() { + super(CombatStyle.RANGE); + } - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (style == CombatStyle.RANGE) { - fireType.getTask().exec(victim, entity); - state.setStyle(null); - DRAGONFIRE.adjustBattleState(entity, victim, state); - state.setStyle(CombatStyle.RANGE); - return; - } - style.getSwingHandler().adjustBattleState(entity, victim, state); - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (style == CombatStyle.RANGE) { + fireType.getTask().exec(victim, entity); + state.setStyle(null); + DRAGONFIRE.adjustBattleState(entity, victim, state); + state.setStyle(CombatStyle.RANGE); + return; + } + style.getSwingHandler().adjustBattleState(entity, victim, state); + } - @Override - public int calculateAccuracy(Entity entity) { - // If in melee combat, calculate attackers accuracy level. - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - // Else calculate attackers accuracy based on their magic level. - return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); - } + @Override + public int calculateAccuracy(Entity entity) { + // If in melee combat, calculate attackers accuracy level. + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateAccuracy(entity); + } + // Else calculate attackers accuracy based on their magic level. + return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); + } - @Override - public int calculateDefence(Entity victim, Entity attacker) { - // If in melee combat, calculate players defense level. - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateDefence(victim, attacker); - } - // Else calculate players defense against attack based on their magic level. - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + // If in melee combat, calculate players defense level. + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateDefence(victim, attacker); + } + // Else calculate players defense against attack based on their magic level. + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public int calculateHit(Entity entity, Entity victim, double modifier) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateHit(entity, victim, modifier); - } - int maxDamage = 60; // Max Possible hit without shield protection - int finalDamage = maxDamage; + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateHit(entity, victim, modifier); + } - if (victim instanceof Player) { - int val = victim.getDragonfireProtection(true); // DragonFireShield Protection + return calculateDragonfireMaxHit(victim, 60, false, 3, true); + } - 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 - } - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT; + } + if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { + entity.getWalkingQueue().reset(); + return InteractionType.STILL_INTERACT; + } + return InteractionType.NO_INTERACT; + } - return finalDamage; - } + @Override + public ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - if (!isProjectileClipped(entity, victim, false)) { - return InteractionType.NO_INTERACT; - } - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { - entity.getWalkingQueue().reset(); - return InteractionType.STILL_INTERACT; - } - return InteractionType.NO_INTERACT; - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } - @Override - public ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + style.getSwingHandler().impact(entity, victim, state); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + style = CombatStyle.RANGE; + int hit = 0; + int ticks = 1; - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } + if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) ) { + if ( RandomFunction.random(10) < 7 ){ + style = CombatStyle.MELEE; + } + } else { + ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; + state.setStyle(style); - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) ) { - if ( RandomFunction.random(10) < 7 ){ - style = CombatStyle.MELEE; - } - } else { - ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); - } + if (isAccurateImpact(entity, victim)) { + int max = calculateHit(entity, victim, 1.0); + state.setMaximumHit(max); + hit = RandomFunction.random(max + 1); + } + state.setEstimatedHit(hit); + return ticks; + } - state.setStyle(style); + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + switch (style) { + case MELEE: + entity.animate(MELEE_ATTACK); + break; + case RANGE: + Projectile.ranged(entity, victim, 450, 20, 36, 50, 15).send(); + entity.animate(fireType.getAnimation()); + break; + default: + break; + } + } - if (isAccurateImpact(entity, victim)) { - int max = calculateHit(entity, victim, 1.0); - state.setMaximumHit(max); - hit = RandomFunction.random(max); - } - state.setEstimatedHit(hit); - return ticks; - } + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (style != CombatStyle.MELEE) { + DRAGONFIRE.visualizeImpact(entity, victim, state); + } else { + style.getSwingHandler().visualizeImpact(entity, victim, state); + } + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - switch (style) { - case MELEE: - entity.animate(MELEE_ATTACK); - break; - case RANGE: - Projectile.ranged(entity, victim, 450, 20, 36, 50, 15).send(); - entity.animate(fireType.getAnimation()); - break; - default: - break; - } - } - - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (style != CombatStyle.MELEE) { - DRAGONFIRE.visualizeImpact(entity, victim, state); - } else { - style.getSwingHandler().visualizeImpact(entity, victim, state); - } - } - - } + } } diff --git a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java index d7dd2c7b5..ab5559616 100644 --- a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java @@ -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 @@ -28,236 +30,216 @@ import core.tools.RandomFunction; @Initializable public final class KingBlackDragonNPC extends AbstractNPC { - /** - * The default spawn location. - */ - private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); + /** + * The default spawn location. + */ + private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); - /** - * The combat swing handler. - */ - private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); + /** + * The combat swing handler. + */ + private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); - - public KingBlackDragonNPC() { - super(-1,null); - } + + public KingBlackDragonNPC() { + super(-1,null); + } - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); - } + @Override + public void finalizeDeath(Entity killer) { + super.finalizeDeath(killer); + BossKillCounter.addtoKillcount((Player) killer, this.getId()); + } - /** - * Constructs a new {@code KingBlackDragonNPC} {@code Object}. - * @param id the id. - * @param l the l. - */ - public KingBlackDragonNPC(int id, Location l) { - super(id, l); - } + /** + * Constructs a new {@code KingBlackDragonNPC} {@code Object}. + * @param id the id. + * @param l the l. + */ + public KingBlackDragonNPC(int id, Location l) { + super(id, l); + } - @Override - public void init() { - super.init(); - configureBossData(); - } + @Override + public void init() { + super.init(); + configureBossData(); + } - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new KingBlackDragonNPC(id, location); - } + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new KingBlackDragonNPC(id, location); + } - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } - @Override - public int[] getIds() { - return new int[] { 50 }; - } + @Override + public int[] getIds() { + return new int[] { 50 }; + } - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatHandler; - } + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatHandler; + } - @Override - public Plugin newInstance(Object arg) throws Throwable { - return super.newInstance(arg); - } + @Override + public Plugin newInstance(Object arg) throws Throwable { + return super.newInstance(arg); + } - /** - * Handles the King Black Dragon's combat swings. - * @author Emperor - */ - static class KBDCombatSwingHandler extends CombatSwingHandler { + /** + * Handles the King Black Dragon's combat swings. + * @author Emperor + */ + static class KBDCombatSwingHandler extends CombatSwingHandler { - /** - * The style. - */ - private CombatStyle style = CombatStyle.RANGE; - - /** - * Dragonfire. - */ - private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 56, null, true); - - /** - * The melee attack animation. - */ - private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - - /** - * The fire type. - */ - private FireType fireType = FireType.FIERY_BREATH; - - /** - * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. + /** + * The style. */ - public KBDCombatSwingHandler() { - super(CombatStyle.RANGE); - } + private CombatStyle style = CombatStyle.RANGE; - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (style == CombatStyle.RANGE) { - fireType.getTask().exec(victim, entity); - state.setStyle(null); - DRAGONFIRE.adjustBattleState(entity, victim, state); - state.setStyle(CombatStyle.RANGE); - return; - } - style.getSwingHandler().adjustBattleState(entity, victim, state); - } + /** + * Dragonfire. + */ + private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 56, null, true); - @Override - public int calculateAccuracy(Entity entity) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); - } + /** + * The melee attack animation. + */ + private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - @Override - public int calculateDefence(Entity victim, Entity attacker) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateDefence(victim, attacker); - } - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } + /** + * The fire type. + */ + private FireType fireType = FireType.FIERY_BREATH; - @Override - public int calculateHit(Entity entity, Entity victim, double modifier) { - 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; - } + /** + * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. + */ + public KBDCombatSwingHandler() { + super(CombatStyle.RANGE); + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - 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) { - entity.getWalkingQueue().reset(); - return InteractionType.STILL_INTERACT; - } - return InteractionType.NO_INTERACT; - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (style == CombatStyle.RANGE) { + fireType.getTask().exec(victim, entity); + state.setStyle(null); + DRAGONFIRE.adjustBattleState(entity, victim, state); + state.setStyle(CombatStyle.RANGE); + return; + } + style.getSwingHandler().adjustBattleState(entity, victim, state); + } - @Override - public ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } + @Override + public int calculateAccuracy(Entity entity) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateAccuracy(entity); + } + return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateDefence(victim, attacker); + } + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateHit(entity, victim, modifier); + } + return calculateDragonfireMaxHit(victim, 56, false, fireType != FireType.FIERY_BREATH ? 10 : 0, true); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; - if (victim.getCenterLocation().withinDistance(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); - } - fireType = FireType.values()[RandomFunction.random(FireType.values().length)]; - state.setStyle(style); - if (isAccurateImpact(entity, victim)) { - int max = calculateHit(entity, victim, 1.0); - state.setMaximumHit(max); - hit = RandomFunction.random(max); - } - state.setEstimatedHit(hit); - ((NPC) entity).getAggressiveHandler().setPauseTicks(2); - return ticks; - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_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; + } + return InteractionType.NO_INTERACT; + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - switch (style) { - case MELEE: - entity.animate(MELEE_ATTACK); - break; - case RANGE: - Projectile.ranged(entity, victim, fireType.getProjectileId(), 20, 36, 50, 15).send(); - entity.animate(fireType.getAnimation()); - break; - default: - break; - } - } + @Override + public ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (style != CombatStyle.MELEE) { - DRAGONFIRE.visualizeImpact(entity, victim, state); - } else { - style.getSwingHandler().visualizeImpact(entity, victim, state); - } - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } - /** - * Gets the fire type. - * @return the type. - */ - public FireType getFireType() { - return fireType; - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + style.getSwingHandler().impact(entity, victim, state); + } - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + style = CombatStyle.RANGE; + int hit = 0; + int ticks = 1; + 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); + } + fireType = FireType.values()[RandomFunction.random(FireType.values().length)]; + state.setStyle(style); + if (isAccurateImpact(entity, victim)) { + int max = calculateHit(entity, victim, 1.0); + state.setMaximumHit(max); + hit = RandomFunction.random(max + 1); + } + state.setEstimatedHit(hit); + ((NPC) entity).getAggressiveHandler().setPauseTicks(2); + return ticks; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + switch (style) { + case MELEE: + entity.animate(MELEE_ATTACK); + break; + case RANGE: + Projectile.ranged(entity, victim, fireType.getProjectileId(), 20, 36, 50, 15).send(); + entity.animate(fireType.getAnimation()); + break; + default: + break; + } + } + + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (style != CombatStyle.MELEE) { + DRAGONFIRE.visualizeImpact(entity, victim, state); + } else { + style.getSwingHandler().visualizeImpact(entity, victim, state); + } + } + + /** + * Gets the fire type. + * @return the type. + */ + public FireType getFireType() { + return fireType; + } + + } } diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index 3155234ea..633f6a965 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -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 return Pair,List>(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 Source +**/ +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 Source +**/ +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