Merge branch 'aggro-fixes' into 'master'

Fixes to NPC stacking and aggression:

See merge request 2009scape/2009scape!321
This commit is contained in:
Ceikry 2021-10-27 19:07:43 +00:00
commit 370636b96b
27 changed files with 259 additions and 82 deletions

View file

@ -27038,6 +27038,7 @@
"name": "Rock lobster",
"defence_level": "100",
"safespot": null,
"movement_radius": "30",
"lifepoints": "150",
"strength_level": "100",
"id": "2889",
@ -27045,6 +27046,25 @@
"range_level": "1",
"attack_level": "100"
},
{
"examine": "A Rock.",
"melee_animation": "2860",
"range_animation": "2860",
"attack_speed": "2",
"defence_animation": "2861",
"weakness": "7",
"magic_animation": "2860",
"death_animation": "2861",
"name": "Large rock",
"defence_level": "100",
"safespot": null,
"lifepoints": "150",
"strength_level": "100",
"id": "2890",
"aggressive": "true",
"range_level": "1",
"attack_level": "100"
},
{
"agg_radius": "12",
"examine": "A sneaky, spiny, subterranean sea-dwelling scamp.",

View file

@ -151,6 +151,11 @@ public final class PCPortalNPC extends AbstractNPC {
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return true;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
updateLifepoints = true;

View file

@ -227,6 +227,7 @@ public final class PestControlActivityPlugin extends ActivityPlugin {
PluginManager.definePlugin(new PCShifterNPC());
PluginManager.definePlugin(new PCSplatterNPC());
PluginManager.definePlugin(new PCSpinnerNPC());
PluginManager.definePlugin(new PCBrawlerNPC());
PluginManager.definePlugin(new PCObjectHandler());
PluginManager.definePlugin(new PestControlSquire());
PluginManager.definePlugin(new VoidSealPlugin());

View file

@ -63,7 +63,7 @@ public final class VoidSealPlugin extends OptionHandler {
* @return {@code True} if so.
*/
private static boolean canTarget(NPC npc) {
return npc instanceof PCDefilerNPC || npc instanceof PCRavagerNPC || npc instanceof PCShifterNPC || npc instanceof PCSpinnerNPC || npc instanceof PCSplatterNPC || npc instanceof PCTorcherNPC;
return npc instanceof PCDefilerNPC || npc instanceof PCRavagerNPC || npc instanceof PCShifterNPC || npc instanceof PCSpinnerNPC || npc instanceof PCSplatterNPC || npc instanceof PCTorcherNPC || npc instanceof PCBrawlerNPC;
}
}

View file

@ -0,0 +1,91 @@
package core.game.content.activity.pestcontrol.monsters;
import core.game.content.activity.pestcontrol.PestControlSession;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.InteractionType;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.player.Player;
import core.game.system.task.Pulse;
import core.game.world.map.Location;
import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics;
import core.tools.RandomFunction;
import rs09.game.node.entity.combat.CombatPulse;
import rs09.game.node.entity.combat.CombatSwingHandler;
import rs09.game.node.entity.combat.handlers.MeleeSwingHandler;
import rs09.game.world.GameWorld;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Handles the pest control brawler NPCs.
* @author Emperor
*/
public final class PCBrawlerNPC extends AbstractNPC {
/**
* The pest control session.
*/
private PestControlSession session;
/**
* Constructs a new {@code PCBrawlerNPC} {@code Object}.
*/
public PCBrawlerNPC() {
super(3772, null);
}
/**
* Constructs a new {@code PCBrawlerNPC} {@code Object}.
* @param id The NPC id.
* @param location The location.
*/
public PCBrawlerNPC(int id, Location location) {
super(id, location);
}
@Override
public void init() {
super.setAggressive(true);
super.init();
super.getDefinition().setCombatDistance(1);
super.walkRadius = 64;
getProperties().getCombatPulse().setStyle(CombatStyle.MELEE);
session = getExtension(PestControlSession.class);
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return true;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);
if (session != null && state != null && entity instanceof Player) {
int total = 0;
if (state.getEstimatedHit() > 0) {
total += state.getEstimatedHit();
}
if (state.getSecondaryHit() > 0) {
total += state.getSecondaryHit();
}
session.addZealGained((Player) entity, total);
}
}
@Override
public AbstractNPC construct(int id, Location location, Object... objects) {
return new PCBrawlerNPC(id, location);
}
@Override
public int[] getIds() {
return new int[] { 3772, 3773, 3774, 3775, 3776 };
}
}

View file

@ -6,6 +6,7 @@ import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.InteractionType;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.game.world.map.MapDistance;
@ -77,6 +78,11 @@ public final class PCDefilerNPC extends AbstractNPC {
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -5,6 +5,7 @@ import core.game.node.Node;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.scenery.Scenery;
import core.game.node.scenery.SceneryBuilder;
@ -171,6 +172,11 @@ public class PCRavagerNPC extends AbstractNPC {
return true;
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -6,6 +6,7 @@ import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.InteractionType;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.system.task.Pulse;
import core.game.world.map.Location;
@ -84,6 +85,11 @@ public final class PCShifterNPC extends AbstractNPC {
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -111,6 +111,11 @@ public final class PCSpinnerNPC extends AbstractNPC {
});
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -75,6 +75,11 @@ public final class PCSplatterNPC extends AbstractNPC {
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -10,6 +10,7 @@ import core.game.node.entity.combat.equipment.SpellType;
import core.game.node.entity.impl.Animator.Priority;
import core.game.node.entity.impl.Projectile;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
import core.game.world.map.Location;
@ -91,6 +92,11 @@ public final class PCTorcherNPC extends AbstractNPC {
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof NPC;
}
@Override
public void onImpact(final Entity entity, BattleState state) {
super.onImpact(entity, state);

View file

@ -98,6 +98,10 @@ public final class TzhaarFightCaveNPC extends AbstractNPC {
}
}
}
@Override
public boolean shouldPreventStacking(Entity mover) {
return mover instanceof TzhaarFightCaveNPC;
}
@Override
public void finalizeDeath(Entity killer) {

View file

@ -58,6 +58,7 @@ public final class SpinolypNPC extends AbstractNPC {
super.init();
super.getLocks().lockMovement(Integer.MAX_VALUE);
setSpell();
getAggressiveHandler().setAllowTolerance(false);
}
/**

View file

@ -24,9 +24,11 @@ import core.game.world.map.Location;
import core.game.world.map.RegionManager;
import core.game.world.map.zone.MapZone;
import core.game.world.map.zone.ZoneBorders;
import core.game.world.map.zone.ZoneBuilder;
import core.game.world.map.zone.ZoneRestriction;
import rs09.game.world.repository.Repository;
import core.game.world.update.flag.context.Animation;
import core.plugin.Initializable;
import core.plugin.Plugin;
import rs09.plugin.PluginManager;
@ -34,6 +36,7 @@ import rs09.plugin.PluginManager;
* Handles the waterbirth dungeon zone.
* @author Vexia
*/
@Initializable
public final class WaterBirthDungeonZone extends MapZone implements Plugin<Object> {
/**
@ -54,6 +57,7 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin<Objec
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ZoneBuilder.configure(this);
return this;
}

View file

@ -29,7 +29,6 @@ public final class WaterBirthIslandZone extends MapZone implements Plugin<Object
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ZoneBuilder.configure(this);
ZoneBuilder.configure(new WaterBirthDungeonZone());
return this;
}

View file

@ -249,6 +249,13 @@ public abstract class Entity extends Node {
return false;
}
/**
* Should this entity prevent the mover from moving through it?
*/
public boolean shouldPreventStacking(Entity mover) {
return false;
}
/**
* Checks an impact before receiving it.
*/

View file

@ -113,7 +113,7 @@ public abstract class CombatSpell extends MagicSpell {
List<Entity> list = new ArrayList<>(20);
list.add(target);
boolean npc = target instanceof NPC;
for (Entity e : npc ? RegionManager.getLocalNpcs(target, 1) : RegionManager.getLocalPlayers(target, 1)) {
for (Entity e : npc ? RegionManager.getSurroundingNPCs(target) : RegionManager.getSurroundingPlayers(target)) {
if (e != target && e != entity && CombatStyle.MAGIC.getSwingHandler().canSwing(entity, e) != InteractionType.NO_INTERACT) {
list.add(e);
}

View file

@ -123,10 +123,10 @@ public final class WalkingQueue {
walk = walk.transform(point.getDiffX(), point.getDiffY(), 0);
if (!entity.getZoneMonitor().move(entity.getLocation(), walk)) {
reset();
if (entity.getPulseManager().isMovingPulse()) {
/*if (entity.getPulseManager().isMovingPulse()) {
entity.getPulseManager().clear(); // TODO: Check for
// bugs
}
}*/
return;
}
}
@ -137,10 +137,10 @@ public final class WalkingQueue {
runPoint = null;
runDirection = -1;
reset();
if (entity.getPulseManager().isMovingPulse()) {
/*if (entity.getPulseManager().isMovingPulse()) {
entity.getPulseManager().clear(); // TODO: Check for
// bugs
}
}*/
}
}
if (runPoint != null) {

View file

@ -63,11 +63,6 @@ public class AggressiveBehavior {
*/
public boolean canSelectTarget(Entity entity, Entity target) {
int regionId = target.getLocation().getRegionId();
if(target instanceof Player) {
if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) {
return false;
}
}
if (!target.isActive() || DeathTask.isDead(target)) {
return false;
}
@ -77,6 +72,9 @@ public class AggressiveBehavior {
if (entity instanceof NPC && target instanceof Player) {
NPC npc = (NPC) entity;
if (npc.getAggressiveHandler() != null && npc.getAggressiveHandler().isAllowTolerance()) {
if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) {
return false;
}
int ticks = GameWorld.getTicks() - npc.getAggressiveHandler().getPlayerTolerance()[target.getIndex()];
if (ticks > 3000) {
npc.getAggressiveHandler().getPlayerTolerance()[target.getIndex()] = GameWorld.getTicks();
@ -86,12 +84,16 @@ public class AggressiveBehavior {
}
}
int level = target.getProperties().getCurrentCombatLevel();
if (level > entity.getProperties().getCurrentCombatLevel() << 1) {
if (level > entity.getProperties().getCurrentCombatLevel() << 1 && !ignoreCombatLevelDifference()) {
return false;
}
return true;
}
public boolean ignoreCombatLevelDifference() {
return false;
}
/**
* Gets the priority flag.
* @param target The target.

View file

@ -83,7 +83,7 @@ public final class AggressiveHandler {
if (target.getAttribute("ignore_aggression", false)) {
return false;
}
if (((Player) target).getRights().equals(Rights.ADMINISTRATOR)) {
if (((Player) target).getRights().equals(Rights.ADMINISTRATOR) && !target.getAttribute("allow_admin_aggression", false)) {
return false;
}
}

View file

@ -93,6 +93,11 @@ public class TormentedDemonNPC extends AbstractNPC {
this.setDefaultBehavior();
}
@Override
public boolean shouldPreventStacking(Entity other) {
return other instanceof TormentedDemonNPC;
}
@Override
public void handleTickActions() {
super.handleTickActions();

View file

@ -17,32 +17,34 @@ import core.tools.RandomFunction;
*/
@Initializable
public final class RockCrabNPC extends AbstractNPC {
/**
* The aggresive behavior.
*/
private static final AggressiveBehavior AGGRO_BEHAVIOR = new AggressiveBehavior() {
@Override
public boolean canSelectTarget(Entity entity, Entity target) {
int regionId = target.getLocation().getRegionId();
if(target instanceof Player){
if(RegionManager.forId(regionId).isTolerated(target.asPlayer())) return false;
}
RockCrabNPC npc = (RockCrabNPC) entity;
if (entity.getLocation().withinDistance(target.getLocation(), 3)) {
npc.aggresor = true;
npc.target = target;
npc.transform(npc.getTransformId());
public boolean ignoreCombatLevelDifference() {
return true;
}
return super.canSelectTarget(entity, target);
@Override
public boolean canSelectTarget(Entity entity, Entity target) {
return super.canSelectTarget(entity, target) &&
entity.getLocation().withinDistance(target.getLocation(), 3);
}
};
@Override
public void onAttack(Entity target) {
this.aggressor = true;
this.target = target;
if(getId() == getOriginalId()) {
this.transform(getOriginalId() - 1);
}
}
/**
* If currently an aggresor.
* If currently an aggressor.
*/
private boolean aggresor;
private boolean aggressor;
/**
* The target.
@ -71,9 +73,10 @@ public final class RockCrabNPC extends AbstractNPC {
@Override
public void handleTickActions() {
super.handleTickActions();
if (aggresor && !inCombat() && target.getLocation().getDistance(this.getLocation()) > 12) {
if ((aggressor && !inCombat() && target.getLocation().getDistance(this.getLocation()) > 12) || isInvisible()) {
reTransform();
aggresor = false;
aggressor = false;
target = null;
getWalkingQueue().reset();
setWalks(false);
}

View file

@ -541,6 +541,9 @@ object RegionManager {
val it = players.iterator()
while (it.hasNext()) {
val p = it.next()
if(p.isInvisible()) {
it.remove()
}
if(!p.location.withinMaxnormDistance(n.location, 1)) {
it.remove()
continue
@ -584,6 +587,9 @@ object RegionManager {
val it = npcs.iterator()
while (it.hasNext()) {
val p = it.next()
if(p.isInvisible()) {
it.remove()
}
if(!p.location.withinMaxnormDistance(n.location, 1)) {
it.remove()
continue

View file

@ -7,6 +7,7 @@ import core.game.world.map.Direction;
import core.game.world.map.Location;
import core.game.world.map.MapDistance;
import core.game.world.map.RegionManager;
import core.game.world.map.path.Pathfinder;
import core.game.world.map.zone.MapZone;
import core.game.world.map.zone.ZoneBorders;
import core.net.packet.PacketRepository;
@ -122,35 +123,17 @@ public final class MultiwayCombatZone extends MapZone {
if (e.getProperties().isNPCWalkable()) {
return true;
}
boolean pestControl = e.getViewport().getRegion().getRegionId() == 10536;
boolean player = e instanceof Player;
if (!player) {
Direction dir = Direction.getDirection(loc, destination);
if (dir.getStepX() != 0 && dir.getStepY() != 0) {
return true; // Allow diagonal steps so people can still "stack"
// npcs (see barraging mummies)
}
}
if (e instanceof NPC || pestControl) {
for (NPC n : RegionManager.getLocalNpcs(e, MapDistance.RENDERING.getDistance() / 2)) {
if (n.isInvisible() || !n.getDefinition().hasAttackOption() || n == e) {
continue;
}
if (player && pestControl && !(n.getId() >= 3772 && n.getId() <= 3776)) {
continue;
}
Location l = n.getLocation();
// TODO: Better support for sizes.
int s = n.size() - 1;
if(n.shouldPreventStacking(e)) {
int s1 = e.size();
int s2 = n.size();
int x = destination.getX();
int y = destination.getY();
if (x > l.getX()) {
x += e.size() - 1;
}
if (y > l.getY()) {
y += e.size() - 1;
}
if (l.getX() <= x && l.getY() <= y && (l.getX() + s) >= x && (l.getY() + s) >= y) {
Location l = n.getLocation();
if(Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) {
return false;
}
}

View file

@ -473,6 +473,18 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){
else -> reject(player, usageStr)
}
}
define("allow_aggro", Command.Privilege.ADMIN) { player, args ->
val usageStr = "Usage: ::allow_aggro true | false"
if(args.size < 2) {
reject(player, usageStr)
}
when(args[1]) {
"true" -> player.setAttribute("allow_admin_aggression", true)
"false" -> player.removeAttribute("allow_admin_aggression")
else -> reject(player, usageStr)
}
}
}
fun showGeSell(player: Player){