Added target validity checking for combat interactions and special cases
This commit is contained in:
+9
-6
@@ -13,6 +13,7 @@ import core.game.world.map.RegionManager;
|
|||||||
import core.game.world.update.flag.context.Graphics;
|
import core.game.world.update.flag.context.Graphics;
|
||||||
import core.tools.RandomFunction;
|
import core.tools.RandomFunction;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,13 +55,15 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
|
|||||||
entity.getProperties().getCombatPulse().stop();
|
entity.getProperties().getCombatPulse().stop();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
List list = victim instanceof NPC ? RegionManager.getSurroundingNPCs(victim, 14, entity) : RegionManager.getSurroundingPlayers(victim, 14, entity);
|
// ! Initial capacity of 14, but has dynamic size? i.e. we are not restricting to a max of 14 targets hit here now
|
||||||
BattleState[] targets = new BattleState[list.size()];
|
List<Entity> targetCandidates = new ArrayList<>(14);
|
||||||
|
targetCandidates.addAll(RegionManager.getSurroundingNPCs(victim, 14, entity));
|
||||||
|
targetCandidates.addAll(RegionManager.getSurroundingPlayers(victim, 14, entity));
|
||||||
|
BattleState[] targets = new BattleState[targetCandidates.size()];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Object o : list) {
|
for (Entity e : targetCandidates) {
|
||||||
Entity e = (Entity) o;
|
if (canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
|
||||||
if (canSwing(entity, e) != InteractionType.NO_INTERACT) {
|
|
||||||
BattleState s = targets[count++] = new BattleState(entity, e);
|
BattleState s = targets[count++] = new BattleState(entity, e);
|
||||||
s.setStyle(CombatStyle.RANGE);
|
s.setStyle(CombatStyle.RANGE);
|
||||||
int hit = 0;
|
int hit = 0;
|
||||||
|
|||||||
+3
-3
@@ -49,7 +49,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void impact(Entity entity, Entity victim, BattleState state) {
|
public void impact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
||||||
@@ -81,7 +81,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (Object o : list) {
|
for (Object o : list) {
|
||||||
Entity e = (Entity) o;
|
Entity e = (Entity) o;
|
||||||
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) {
|
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
|
||||||
BattleState s = targets[count++] = new BattleState(entity, e);
|
BattleState s = targets[count++] = new BattleState(entity, e);
|
||||||
int hit = 0;
|
int hit = 0;
|
||||||
if (isAccurateImpact(entity, e)) {
|
if (isAccurateImpact(entity, e)) {
|
||||||
@@ -103,7 +103,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
||||||
|
|||||||
+3
-3
@@ -49,7 +49,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void impact(Entity entity, Entity victim, BattleState state) {
|
public void impact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
||||||
@@ -82,7 +82,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (Object o : list) {
|
for (Object o : list) {
|
||||||
Entity e = (Entity) o;
|
Entity e = (Entity) o;
|
||||||
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) {
|
if (CombatStyle.RANGE.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT && e.isAttackable(entity, CombatStyle.RANGE, false)) {
|
||||||
BattleState s = targets[count++] = new BattleState(entity, e);
|
BattleState s = targets[count++] = new BattleState(entity, e);
|
||||||
int hit = 0;
|
int hit = 0;
|
||||||
hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1);
|
hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1);
|
||||||
@@ -102,7 +102,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
||||||
|
|||||||
+6
-3
@@ -90,7 +90,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
|
|||||||
*/
|
*/
|
||||||
private BattleState[] getTargets(Entity entity, Entity victim, BattleState state) {
|
private BattleState[] getTargets(Entity entity, Entity victim, BattleState state) {
|
||||||
if (!entity.getProperties().isMultiZone() || !victim.getProperties().isMultiZone()) {
|
if (!entity.getProperties().isMultiZone() || !victim.getProperties().isMultiZone()) {
|
||||||
return new BattleState[] { state };
|
return new BattleState[]{state};
|
||||||
}
|
}
|
||||||
Location vl = victim.getLocation();
|
Location vl = victim.getLocation();
|
||||||
int x = vl.getX();
|
int x = vl.getX();
|
||||||
@@ -102,6 +102,9 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
|
|||||||
if (n instanceof Familiar) {
|
if (n instanceof Familiar) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!n.isAttackable(entity, CombatStyle.MELEE, false)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (n.getLocation().equals(vl.transform(dir.getStepY(), dir.getStepX(), 0)) || n.getLocation().equals(vl.transform(-dir.getStepY(), -dir.getStepX(), 0))) {
|
if (n.getLocation().equals(vl.transform(dir.getStepY(), dir.getStepX(), 0)) || n.getLocation().equals(vl.transform(-dir.getStepY(), -dir.getStepX(), 0))) {
|
||||||
l.add(new BattleState(entity, n));
|
l.add(new BattleState(entity, n));
|
||||||
if (l.size() >= 3) {
|
if (l.size() >= 3) {
|
||||||
@@ -127,7 +130,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void impact(Entity entity, Entity victim, BattleState state) {
|
public void impact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
s.getVictim().getImpactHandler().handleImpact(entity, s.getEstimatedHit(), CombatStyle.MELEE, s);
|
||||||
@@ -146,7 +149,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
|
||||||
if (state.getTargets() != null) {
|
if (state != null && state.getTargets() != null) {
|
||||||
for (BattleState s : state.getTargets()) {
|
for (BattleState s : state.getTargets()) {
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
s.getVictim().animate(victim.getProperties().getDefenceAnimation());
|
||||||
|
|||||||
@@ -274,47 +274,57 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
||||||
|
if (isInvalidTarget(entity, style, message)) return false;
|
||||||
|
return super.isAttackable(entity, style, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||||
|
return !isInvalidTarget(target, style, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInvalidTarget(Entity entity, CombatStyle style, boolean message) {
|
||||||
if (entity == owner) {
|
if (entity == owner) {
|
||||||
if (message) {
|
if (message) {
|
||||||
owner.getPacketDispatch().sendMessage("You can't just betray your own familiar like that!");
|
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
if (entity instanceof Player) {
|
if (entity instanceof Player) {
|
||||||
if (!owner.isAttackable(entity, style, message)) {
|
if (!owner.isAttackable(entity, style, message)) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!getProperties().isMultiZone()) {
|
if (!getProperties().isMultiZone()) {
|
||||||
if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) {
|
if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) {
|
||||||
if (message) {
|
if (message) {
|
||||||
((Player) entity).getPacketDispatch().sendMessage("You have to be in multicombat to attack a player's familiar.");
|
sendMessage((Player) entity, "You have to be in multicombat to attack a player's familiar.");
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
if (entity instanceof Player) {
|
if (entity instanceof Player) {
|
||||||
if (message) {
|
if (message) {
|
||||||
((Player) entity).getPacketDispatch().sendMessage("This familiar is not in the a multicombat zone.");
|
sendMessage((Player) entity, "This familiar is not in the a multicombat zone.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
if (entity instanceof Player) {
|
if (entity instanceof Player) {
|
||||||
if (!((Player) entity).getSkullManager().isWilderness()) {
|
if (!((Player) entity).getSkullManager().isWilderness()) {
|
||||||
if (message) {
|
if (message) {
|
||||||
((Player) entity).getPacketDispatch().sendMessage("You have to be in the wilderness to attack a player's familiar.");
|
sendMessage((Player) entity, "You have to be in the wilderness to attack a player's familiar.");
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
if (!owner.getSkullManager().isWilderness()) {
|
if (!owner.getSkullManager().isWilderness()) {
|
||||||
if (message) {
|
if (message) {
|
||||||
((Player) entity).getPacketDispatch().sendMessage("This familiar's owner is not in the wilderness.");
|
sendMessage((Player) entity, "This familiar's owner is not in the wilderness.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return super.isAttackable(entity, style, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegionInactivity() {
|
public void onRegionInactivity() {
|
||||||
|
|||||||
@@ -490,9 +490,24 @@ public abstract class Entity extends Node {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an entity can continue its attack.
|
* Checks if an entity can continue its attack.
|
||||||
* @param target the target.
|
*
|
||||||
* @param style the style.
|
* <p>This method is called during attack validation to determine if the attacking entity should be
|
||||||
* @return {@code True} if so.
|
* allowed to continue attacking the target. It's invoked by
|
||||||
|
* {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)}
|
||||||
|
* as part of the attack validation chain.</p>
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns {@code true}, allowing the attack to proceed.
|
||||||
|
* <b>Subclasses should override this method to implement entity-specific attack restrictions.</b></p>
|
||||||
|
*
|
||||||
|
* <p>This method is distinct from {@link #isAttackable(Entity, CombatStyle, boolean)}, which
|
||||||
|
* checks if this entity can be attacked by another entity. The {@code continueAttack} method
|
||||||
|
* checks if this entity can attack the given target.</p>
|
||||||
|
*
|
||||||
|
* @param target the target entity being attacked
|
||||||
|
* @param style the combat style being used
|
||||||
|
* @param message whether to send a message to the player explaining why the attack cannot continue
|
||||||
|
*
|
||||||
|
* @return {@code true} if the entity can continue attacking the target
|
||||||
*/
|
*/
|
||||||
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public abstract class CombatSpell extends MagicSpell {
|
|||||||
if (e == target || e == entity) {
|
if (e == target || e == entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) == InteractionType.NO_INTERACT) {
|
if (CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) == InteractionType.NO_INTERACT || !e.isAttackable(entity, CombatStyle.MAGIC, false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (e instanceof Familiar) {
|
if (e instanceof Familiar) {
|
||||||
|
|||||||
@@ -409,6 +409,20 @@ public class NPC extends Entity {
|
|||||||
return super.isAttackable(entity, style, message);
|
return super.isAttackable(entity, style, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||||
|
if (isInvisible()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (task != null && target instanceof Player && task.levelReq > target.getSkills().getLevel(Skills.SLAYER)) {
|
||||||
|
if (message) {
|
||||||
|
((Player) target).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return behavior.canBeAttackedBy(this, target, style, message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDragonfireProtection(boolean fire) {
|
public int getDragonfireProtection(boolean fire) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -766,6 +766,30 @@ public class Player extends Entity {
|
|||||||
return super.isAttackable(entity, style, message);
|
return super.isAttackable(entity, style, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||||
|
if (target instanceof NPC
|
||||||
|
&& !((NPC) target).getDefinition().hasAction("attack")
|
||||||
|
&& !((NPC) target).isIgnoreAttackRestrictions(this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (target instanceof Player) {
|
||||||
|
Player p = (Player) target;
|
||||||
|
if (p.getSkullManager().isWilderness() && skullManager.isWilderness()) {
|
||||||
|
if (GameWorld.getSettings() == null || !GameWorld.getSettings().getWild_pvp_enabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (p.getSkullManager().hasWildernessProtection()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !skullManager.hasWildernessProtection();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPoisonImmune() {
|
public boolean isPoisonImmune() {
|
||||||
return timers.getTimer("poison:immunity") != null;
|
return timers.getTimer("poison:immunity") != null;
|
||||||
|
|||||||
@@ -130,12 +130,25 @@ public abstract class MapZone implements Zone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the entity is able to continue attacking the target.
|
* Checks if the entity is able to continue attacking the target within this zone.
|
||||||
* @param e the attacking entity.
|
*
|
||||||
* @param target The target.
|
* <p>This method is called during attack validation to determine if zone-specific rules
|
||||||
* @param style The combat style used.
|
* allow the attack to proceed. It's invoked by
|
||||||
* @param message If a message should be send.
|
* {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)}
|
||||||
* @return {@code True} if so.
|
* for each zone the attacking entity is in.</p>
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns {@code true}, allowing the attack to proceed.
|
||||||
|
* <b>Subclasses should override this method to implement zone-specific attack restrictions.</b></p>
|
||||||
|
* <p>This method is part of the attack validation chain:
|
||||||
|
* {@link Entity#isAttackable(Entity, CombatStyle, boolean)} ->
|
||||||
|
* {@link core.game.world.map.zone.ZoneMonitor#continueAttack(Node, CombatStyle, boolean)} ->
|
||||||
|
* {@code MapZone.continueAttack()} + {@link Entity#continueAttack(Entity, CombatStyle, boolean)}</p>
|
||||||
|
*
|
||||||
|
* @param e the attacking entity
|
||||||
|
* @param target the target node being attacked
|
||||||
|
* @param style the combat style being used
|
||||||
|
* @param message whether to send a message to the player explaining why the attack cannot continue
|
||||||
|
* @return {@code true} if the zone allows the attack to continue
|
||||||
*/
|
*/
|
||||||
public boolean continueAttack(Entity e, Node target, CombatStyle style, boolean message) {
|
public boolean continueAttack(Entity e, Node target, CombatStyle style, boolean message) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user