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,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;
}
}
}
@@ -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
}
}
}
@@ -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);
}
}
}
}
}
@@ -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<Object> newInstance(Object arg) throws Throwable {
return super.newInstance(arg);
}
@Override
public Plugin<Object> 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;
}
}
}
+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