mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-15 11:00:17 -07:00
Implemented ContentInterface-based NPC Scripting
Obsoleted AbstractNPC Implemented desert bandits Rewrote a handful of existing NPCs into NPCBehaviors (e.g. rock slugs, nechryaels, water fiend and more)
This commit is contained in:
parent
f0d7b82bf9
commit
9a4b933976
18 changed files with 490 additions and 563 deletions
100
Server/src/main/content/global/skill/slayer/NechryaelBehavior.kt
Normal file
100
Server/src/main/content/global/skill/slayer/NechryaelBehavior.kt
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
package content.global.skill.slayer
|
||||
|
||||
import core.api.animate
|
||||
import core.api.getAttribute
|
||||
import core.api.setAttribute
|
||||
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.DeathTask
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.GameWorld
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
class NechryaelBehavior : NPCBehavior(*Tasks.NECHRYAELS.npcs) {
|
||||
private val ATTR_SPAWNS = "deathSpawns"
|
||||
private val ATTR_NEXTSPAWN = "deathSpawnNextTick"
|
||||
|
||||
override fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
|
||||
if (attacker !is Player) return
|
||||
if (!canSpawnDeathspawn(self)) return
|
||||
if (!RandomFunction.roll(5)) return
|
||||
spawnDeathSpawn(self, attacker)
|
||||
}
|
||||
|
||||
fun spawnDeathSpawn(self: NPC, player: Player) {
|
||||
val npc = NPC.create(NPCs.DEATH_SPAWN_1614, self.location.transform(self.direction, 1))
|
||||
setAttribute(npc, "parent", self)
|
||||
setAttribute(npc, "target", player)
|
||||
npc.isRespawn = false
|
||||
npc.init()
|
||||
addSpawn(self, npc)
|
||||
setNextSpawn(self)
|
||||
animate(self, 9491)
|
||||
}
|
||||
|
||||
fun canSpawnDeathspawn(self: NPC) : Boolean {
|
||||
if (getSpawns(self).size >= 2) {
|
||||
setNextSpawn(self)
|
||||
return false
|
||||
}
|
||||
return getNextSpawn(self) <= GameWorld.ticks
|
||||
}
|
||||
|
||||
fun getNextSpawn(self: NPC) : Int {
|
||||
return getAttribute(self, ATTR_NEXTSPAWN, 0)
|
||||
}
|
||||
|
||||
fun setNextSpawn(self: NPC) {
|
||||
setAttribute(self, ATTR_NEXTSPAWN, GameWorld.ticks + 50)
|
||||
}
|
||||
|
||||
fun getSpawns(self: NPC) : ArrayList<NPC> {
|
||||
return getAttribute(self, ATTR_SPAWNS, ArrayList())
|
||||
}
|
||||
|
||||
fun addSpawn(self: NPC, spawn: NPC) {
|
||||
val list = getSpawns(self)
|
||||
list.add(spawn)
|
||||
setAttribute(self, ATTR_SPAWNS, list)
|
||||
}
|
||||
|
||||
fun removeSpawn(self: NPC, spawn: NPC) {
|
||||
val list = getSpawns(self)
|
||||
list.remove(spawn)
|
||||
setAttribute(self, ATTR_SPAWNS, list)
|
||||
}
|
||||
}
|
||||
|
||||
class DeathspawnBehavior : NPCBehavior(NPCs.DEATH_SPAWN_1614) {
|
||||
override fun onCreation(self: NPC) {
|
||||
setAttribute(self, "despawn-time", GameWorld.ticks + 100)
|
||||
val target = getAttribute<Player?>(self, "target", null) ?: return
|
||||
self.attack(target)
|
||||
}
|
||||
|
||||
override fun onRemoval(self: NPC) {
|
||||
val parent = getAttribute<NPC?>(self, "parent", null) ?: return
|
||||
if (parent.behavior !is NechryaelBehavior) return
|
||||
parent.behavior.removeSpawn(parent, self)
|
||||
}
|
||||
|
||||
override fun tick(self: NPC): Boolean {
|
||||
val target = getAttribute<Player?>(self, "target", null) ?: return true
|
||||
|
||||
if (!target.isActive || DeathTask.isDead(target) || getAttribute(self, "despawn-time", 0) <= GameWorld.ticks)
|
||||
self.clear()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun shouldIgnoreMultiRestrictions(self: NPC, victim: Entity): Boolean {
|
||||
return victim == getAttribute<Player?>(self, "target", null)
|
||||
}
|
||||
|
||||
override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean {
|
||||
return attacker == getAttribute<Player?>(self, "target", null)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
package content.global.skill.slayer;
|
||||
|
||||
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.npc.AbstractNPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.plugin.Initializable;
|
||||
import core.tools.RandomFunction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles the nechryael npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class NechryaelNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* The death spawn id.
|
||||
*/
|
||||
private static final int DEATH_SPAWN = 1614;
|
||||
|
||||
/**
|
||||
* The death spawn npcs.
|
||||
*/
|
||||
private List<DeathSpawnNPC> spawns = new ArrayList<>(10);
|
||||
|
||||
/**
|
||||
* The next spawn time.
|
||||
*/
|
||||
private int nextSpawn;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code NechryaelNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public NechryaelNPC(int id, Location location) {
|
||||
super(id, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code NechryaelNPC} {@code Object}.
|
||||
*/
|
||||
public NechryaelNPC() {
|
||||
super(0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity entity, final BattleState state) {
|
||||
super.onImpact(entity, state);
|
||||
if (entity instanceof Player && RandomFunction.random(5) == 1) {
|
||||
spawn((Player) entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a death spawn.
|
||||
* @param player the player.
|
||||
*/
|
||||
public void spawn(Player player) {
|
||||
if (!canSpawn()) {
|
||||
return;
|
||||
}
|
||||
final DeathSpawnNPC spawn = new DeathSpawnNPC(DEATH_SPAWN, getLocation().transform(getDirection(), 1), player, this);
|
||||
spawn.init();
|
||||
setSpawnTime();
|
||||
spawns.add(spawn);
|
||||
animate(Animation.create(9491));
|
||||
spawn.getProperties().getCombatPulse().attack(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next spawn time.
|
||||
*/
|
||||
private void setSpawnTime() {
|
||||
nextSpawn = GameWorld.getTicks() + 50;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a death spawn can spawn.
|
||||
* @return {@code True} if so.
|
||||
*/
|
||||
private boolean canSpawn() {
|
||||
if (spawns.size() >= 2) {
|
||||
setSpawnTime();
|
||||
return false;
|
||||
}
|
||||
return nextSpawn < GameWorld.getTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new NechryaelNPC(id, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return Tasks.NECHRYAELS.getNpcs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the death spawn npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
public static final class DeathSpawnNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* The player binded with the death spawn.
|
||||
*/
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* The parent npc.
|
||||
*/
|
||||
private final NechryaelNPC parent;
|
||||
|
||||
/**
|
||||
* The time until its cleared.
|
||||
*/
|
||||
private int time;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code DeathSpawnNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public DeathSpawnNPC(int id, Location location, final Player player, NechryaelNPC parent) {
|
||||
super(id, location);
|
||||
this.player = player;
|
||||
this.parent = parent;
|
||||
this.setRespawn(false);
|
||||
this.time = GameWorld.getTicks() + 120;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTickActions() {
|
||||
super.handleTickActions();
|
||||
if (!inCombat() || !player.inCombat()) {
|
||||
getProperties().getCombatPulse().attack(player);
|
||||
}
|
||||
if (time < GameWorld.getTicks() || !player.isActive() || player.getLocation().getDistance(getLocation()) > 15) {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
parent.spawns.remove(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new DeathSpawnNPC(id, location, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
||||
if (entity instanceof Player) {
|
||||
final Player t = (Player) entity;
|
||||
if (t != player) {
|
||||
if(message) {
|
||||
t.getPacketDispatch().sendMessage("This isn't spawned for you.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.isAttackable(entity, style, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIgnoreMultiBoundaries(Entity victim) {
|
||||
return victim == player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoisonImmune() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return new int[] { DEATH_SPAWN };
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Server/src/main/content/global/skill/slayer/RockSlug.kt
Normal file
50
Server/src/main/content/global/skill/slayer/RockSlug.kt
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package content.global.skill.slayer
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.BattleState
|
||||
import core.game.node.entity.combat.ImpactHandler
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.Player
|
||||
import org.rs09.consts.Items
|
||||
import java.lang.Integer.max
|
||||
|
||||
class RockSlug : NPCBehavior(*Tasks.ROCK_SLUGS.ids), InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onUseWith(IntType.NPC, Items.BAG_OF_SALT_4161, *ids, handler = ::handleSaltUsage)
|
||||
}
|
||||
|
||||
override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
|
||||
val lifepoints = self.skills.lifepoints
|
||||
if (state.estimatedHit + max(state.secondaryHit, 0) > lifepoints - 1) {
|
||||
state.estimatedHit = lifepoints - 1
|
||||
state.secondaryHit = -1
|
||||
setAttribute(self, "shouldRun", true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(self: NPC): Boolean {
|
||||
if (getAttribute(self, "shouldRun", false)){
|
||||
self.properties.combatPulse.stop()
|
||||
forceWalk(self, self.properties.spawnLocation, "smart")
|
||||
removeAttribute(self, "shouldRun")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun handleSaltUsage(player: Player, used: Node, with: Node) : Boolean {
|
||||
if (with !is NPC) return false
|
||||
if (!removeItem(player, used.id)) return false
|
||||
if (with.skills.lifepoints >= 5)
|
||||
sendMessage(player, "Your bag of salt is ineffective. The Rockslug is not weak enough.")
|
||||
else {
|
||||
sendMessage(player, "The Rockslug shrivels up and dies.")
|
||||
with.impactHandler.manualHit(player, with.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
package content.global.skill.slayer;
|
||||
|
||||
import core.game.interaction.NodeUsageEvent;
|
||||
import core.game.interaction.UseWithHandler;
|
||||
import core.game.node.entity.combat.BattleState;
|
||||
import core.game.node.entity.combat.ImpactHandler.HitsplatType;
|
||||
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.item.Item;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.path.Pathfinder;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
import core.plugin.ClassScanner;
|
||||
|
||||
/**
|
||||
* Handles the interactions of a rock slug.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class RockSlugPlugin implements Plugin<Object> {
|
||||
|
||||
/**
|
||||
* The bag of salt item.
|
||||
*/
|
||||
private static final Item SALT = new Item(4161, 1);
|
||||
|
||||
/**
|
||||
* The rockslug npc ids.
|
||||
*/
|
||||
private static final int[] IDS = new int[] { 1631, 1632 };
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
ClassScanner.definePlugin(new RockSlugNPC());
|
||||
ClassScanner.definePlugin(new SaltBagHandler());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fireEvent(String identifier, Object... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The use with handler for the bag of salt on a rock slug.
|
||||
* @author Vexia
|
||||
*/
|
||||
public final class SaltBagHandler extends UseWithHandler {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code SaltBagHandler} {@code Object}.
|
||||
*/
|
||||
public SaltBagHandler() {
|
||||
super(SALT.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
for (int id : IDS) {
|
||||
addHandler(id, NPC_TYPE, this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(NodeUsageEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final NPC npc = (NPC) event.getUsedWith();
|
||||
player.getInventory().remove(SALT);
|
||||
if (npc.getSkills().getLifepoints() < 10) {
|
||||
npc.getImpactHandler().manualHit(player, npc.getSkills().getLifepoints(), HitsplatType.NORMAL);
|
||||
player.getPacketDispatch().sendMessage("The rockslug shrivels up and dies.");
|
||||
} else {
|
||||
player.sendMessage("Your bag of salt is ineffective. The Rockslug is not weak enough.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The rock slug npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
public final class RockSlugNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code RockSlugNPC} {@code Object}.
|
||||
*/
|
||||
public RockSlugNPC() {
|
||||
super(-1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code RockSlugNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public RockSlugNPC(int id, Location location) {
|
||||
super(id, location, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new RockSlugNPC(id, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkImpact(BattleState state) {
|
||||
super.checkImpact(state);
|
||||
int lifepoints = getSkills().getLifepoints();
|
||||
boolean run = false;
|
||||
if (state.getEstimatedHit() > -1) {
|
||||
lifepoints -= state.getEstimatedHit();
|
||||
if (lifepoints < 1) {
|
||||
run = true;
|
||||
state.setEstimatedHit(lifepoints - 1);
|
||||
}
|
||||
if (state.getEstimatedHit() < 0) {
|
||||
state.setEstimatedHit(0);
|
||||
}
|
||||
}
|
||||
if (state.getSecondaryHit() > -1) {
|
||||
lifepoints -= state.getSecondaryHit();
|
||||
if (lifepoints < 1) {
|
||||
run = true;
|
||||
state.setSecondaryHit(lifepoints - 1);
|
||||
}
|
||||
if (state.getSecondaryHit() < 0) {
|
||||
state.setSecondaryHit(0);
|
||||
}
|
||||
}
|
||||
if (run) {
|
||||
getProperties().getCombatPulse().stop();
|
||||
Pathfinder.find(getLocation(), getProperties().getSpawnLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return IDS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package content.global.skill.slayer
|
||||
|
||||
import content.global.handlers.item.equipment.special.DragonfireSwingHandler
|
||||
import core.api.EquipmentSlot
|
||||
import core.api.getItemFromEquipment
|
||||
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.CombatSwingHandler
|
||||
import core.game.node.entity.combat.MultiSwingHandler
|
||||
import core.game.node.entity.combat.equipment.SwitchAttack
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.prayer.PrayerType
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class SkeletalWyvernBehavior : NPCBehavior(*Tasks.SKELETAL_WYVERN.ids) {
|
||||
/**
|
||||
* The combat swing handler.
|
||||
*/
|
||||
private val COMBAT_HANDLER = MultiSwingHandler(SwitchAttack(CombatStyle.MELEE.swingHandler, Animation(2985)), SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(2989), Graphics(499)), DragonfireSwingHandler.get(false, 54, Animation(2988), Graphics(501), null, null, false))
|
||||
|
||||
/**
|
||||
* The combat swing handler for far combat (5+ tile distance)
|
||||
*/
|
||||
private val COMBAT_HANDLER_FAR = MultiSwingHandler(SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(2989), Graphics(499)))
|
||||
|
||||
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
|
||||
|
||||
return if (victim.location.getDistance(self.location) >= 5)
|
||||
COMBAT_HANDLER_FAR
|
||||
else
|
||||
COMBAT_HANDLER
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package content.global.skill.slayer;
|
||||
|
||||
import core.game.container.impl.EquipmentContainer;
|
||||
import core.game.node.entity.combat.BattleState;
|
||||
import core.game.node.entity.combat.CombatStyle;
|
||||
import core.game.node.entity.combat.equipment.SwitchAttack;
|
||||
import content.global.handlers.item.equipment.special.DragonfireSwingHandler;
|
||||
import core.game.node.entity.npc.AbstractNPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.prayer.PrayerType;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable;
|
||||
import core.tools.RandomFunction;
|
||||
import core.game.node.entity.combat.CombatSwingHandler;
|
||||
import core.game.node.entity.combat.MultiSwingHandler;
|
||||
|
||||
/**
|
||||
* Handles the skeletal wyvern npc.
|
||||
* @author Vexia
|
||||
* @author Splinter
|
||||
* @version 1.0
|
||||
*/
|
||||
@Initializable
|
||||
public final class SkeletalWyvernNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* The combat swing handler.
|
||||
*/
|
||||
private static final MultiSwingHandler COMBAT_HANDLER = new MultiSwingHandler(new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(2985)), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(2989), new Graphics(499)), DragonfireSwingHandler.get(false, 54, new Animation(2988), new Graphics(501), null, null, false));
|
||||
|
||||
/**
|
||||
* The combat swing handler for far combat (5+ tile distance)
|
||||
*/
|
||||
private static final MultiSwingHandler COMBAT_HANDLER_FAR = new MultiSwingHandler(new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(2989), new Graphics(499)));
|
||||
|
||||
/**
|
||||
* Constructs a new {@code SkeletalWyvernNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public SkeletalWyvernNPC(int id, Location location) {
|
||||
super(id, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code SkeletalWyvernNPC} {@code Object}.
|
||||
*/
|
||||
public SkeletalWyvernNPC() {
|
||||
super(0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new SkeletalWyvernNPC(id, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendImpact(BattleState state) {
|
||||
if (state.getStyle() == CombatStyle.MAGIC && state.getVictim() != null && state.getVictim().isPlayer()) {
|
||||
Player p = state.getVictim().asPlayer();
|
||||
Item item = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD);
|
||||
if (item != null && (item.getId() == 2890 || item.getId() == 9731 || item.getId() == 11283) && state.getEstimatedHit() > 10) {
|
||||
state.setEstimatedHit(RandomFunction.random(10));
|
||||
}
|
||||
}
|
||||
Player p = state.getVictim().asPlayer();
|
||||
Item item = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD);
|
||||
if(state.getVictim().getLocation().getDistance(state.getAttacker().getLocation()) >= 5
|
||||
&& state.getVictim().asPlayer().getPrayer().get(PrayerType.PROTECT_FROM_MAGIC) && (item.getId() == 2890 || item.getId() == 9731 || item.getId() == 11283)){
|
||||
state.setEstimatedHit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CombatSwingHandler getSwingHandler(boolean swing) {
|
||||
if (this.getProperties().getCombatPulse().getVictim() != null && this.getProperties().getCombatPulse().getVictim().getLocation().getDistance(this.getLocation()) >= 5){
|
||||
return COMBAT_HANDLER_FAR;
|
||||
}
|
||||
return COMBAT_HANDLER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return Tasks.SKELETAL_WYVERN.getNpcs();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package content.global.skill.slayer
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.BattleState
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
class TurothBehavior : NPCBehavior(*Tasks.TUROTHS.ids) {
|
||||
override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
|
||||
if (attacker is Player) {
|
||||
if (!SlayerUtils.hasBroadWeaponEquipped(attacker, state))
|
||||
state.neutralizeHits()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package content.global.skill.slayer;
|
||||
|
||||
import core.game.node.entity.combat.BattleState;
|
||||
import core.game.node.entity.npc.AbstractNPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.world.map.Location;
|
||||
import core.plugin.Initializable;
|
||||
|
||||
/**
|
||||
* Handles the turoth npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class TurothNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code TurothNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public TurothNPC(int id, Location location) {
|
||||
super(id, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code TurothNPC} {@code Object}.
|
||||
*/
|
||||
public TurothNPC() {
|
||||
super(0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new TurothNPC(id, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkImpact(final BattleState state) {
|
||||
super.checkImpact(state);
|
||||
boolean effective = false;
|
||||
if (state.getAttacker() instanceof Player) {
|
||||
final Player player = (Player) state.getAttacker();
|
||||
effective = SlayerUtils.hasBroadWeaponEquipped(player, state);
|
||||
}
|
||||
if (!effective) {
|
||||
state.setEstimatedHit(0);
|
||||
if (state.getSecondaryHit() > 0) {
|
||||
state.setSecondaryHit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return new int[] { 1626, 1627, 1628, 1629, 1630 };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package content.global.skill.slayer;
|
||||
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.CombatStyle;
|
||||
import core.game.node.entity.combat.equipment.SwitchAttack;
|
||||
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.world.map.Location;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.plugin.Initializable;
|
||||
import core.game.node.entity.combat.CombatSwingHandler;
|
||||
import core.game.node.entity.combat.MultiSwingHandler;
|
||||
|
||||
/**
|
||||
* Handles the water fiend npc.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class WaterFiendNPC extends AbstractNPC {
|
||||
|
||||
/**
|
||||
* Handles the combat.
|
||||
*/
|
||||
private final CombatSwingHandler combatAction = new MultiSwingHandler(true, new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(1581, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 15, 30, 50, 50, 14, 255)), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(1581, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 15, 30, 50, 50, 14, 255)));
|
||||
|
||||
/**
|
||||
* Constructs a new {@code WaterFiendNPC} {@code Object}.
|
||||
* @param id the id.
|
||||
* @param location the location.
|
||||
*/
|
||||
public WaterFiendNPC(int id, Location location) {
|
||||
super(id, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code WaterFiendNPC} {@code Object}.
|
||||
*/
|
||||
public WaterFiendNPC() {
|
||||
super(0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNPC construct(int id, Location location, Object... objects) {
|
||||
return new WaterFiendNPC(id, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CombatSwingHandler getSwingHandler(boolean swing) {
|
||||
return combatAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIds() {
|
||||
return Tasks.WATERFIENDS.getNpcs();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package content.global.skill.slayer
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.CombatStyle
|
||||
import core.game.node.entity.combat.CombatSwingHandler
|
||||
import core.game.node.entity.combat.MultiSwingHandler
|
||||
import core.game.node.entity.combat.equipment.SwitchAttack
|
||||
import core.game.node.entity.impl.Animator.Priority
|
||||
import core.game.node.entity.impl.Projectile
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.world.update.flag.context.Animation
|
||||
|
||||
class WaterfiendBehavior : NPCBehavior(*Tasks.WATERFIENDS.ids) {
|
||||
private val combatHandler = MultiSwingHandler(
|
||||
true,
|
||||
SwitchAttack(
|
||||
CombatStyle.MAGIC.swingHandler,
|
||||
Animation(1581, Priority.HIGH),
|
||||
null,
|
||||
null,
|
||||
Projectile.create(
|
||||
null as Entity?,
|
||||
null,
|
||||
500,
|
||||
15,
|
||||
30,
|
||||
50,
|
||||
50,
|
||||
14,
|
||||
255
|
||||
)
|
||||
),
|
||||
SwitchAttack(
|
||||
CombatStyle.RANGE.swingHandler,
|
||||
Animation(1581, Priority.HIGH),
|
||||
null,
|
||||
null,
|
||||
Projectile.create(
|
||||
null as Entity?,
|
||||
null,
|
||||
16,
|
||||
15,
|
||||
30,
|
||||
50,
|
||||
50,
|
||||
14,
|
||||
255
|
||||
)
|
||||
)
|
||||
)
|
||||
override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler {
|
||||
return combatHandler
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +229,7 @@ public enum Pets {
|
|||
// BABY_GOLD_CHINCHOMPA(14826, -1, -1, 8658, -1, -1, 0.0, 1),
|
||||
// HERON(14827, -1, -1, 8647, -1, -1, 0.0, 1),
|
||||
// TZREK_JAD(14828, -1, -1, 8650, -1, -1, 0.0, 1);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The baby pets mapping.
|
||||
|
|
@ -451,12 +451,12 @@ public enum Pets {
|
|||
*/
|
||||
public int getNpcId(int stage) {
|
||||
switch (stage) {
|
||||
case 0:
|
||||
return babyNpcId;
|
||||
case 1:
|
||||
return grownNpcId;
|
||||
case 2:
|
||||
return overgrownNpcId;
|
||||
case 0:
|
||||
return babyNpcId;
|
||||
case 1:
|
||||
return grownNpcId;
|
||||
case 2:
|
||||
return overgrownNpcId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -468,13 +468,13 @@ public enum Pets {
|
|||
*/
|
||||
public int getItemId(int stage) {
|
||||
switch (stage) {
|
||||
case 0:
|
||||
return babyItemId;
|
||||
case 1:
|
||||
return grownItemId;
|
||||
case 2:
|
||||
return overgrownItemId;
|
||||
case 0:
|
||||
return babyItemId;
|
||||
case 1:
|
||||
return grownItemId;
|
||||
case 2:
|
||||
return overgrownItemId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package content.region.desert.bandits.handlers
|
||||
|
||||
import core.api.*
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.BattleState
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.world.map.RegionManager
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
class BanditBehavior : NPCBehavior(NPCs.BANDIT_1926) {
|
||||
override fun tick(self: NPC): Boolean {
|
||||
if (!self.inCombat() && RandomFunction.roll(3) && getWorldTicks() % 5 == 0) {
|
||||
val players = RegionManager.getLocalPlayers(self, 5)
|
||||
for (player in players) {
|
||||
if (player.inCombat()) continue
|
||||
if (hasGodItem(player, God.SARADOMIN)) {
|
||||
sendChat(self, "Prepare to die, Saradominist scum!")
|
||||
self.attack(player)
|
||||
break
|
||||
}
|
||||
else if (hasGodItem(player, God.ZAMORAK)) {
|
||||
sendChat(self, "Prepare to die, Zamorakian scum!")
|
||||
self.attack(player)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
|
||||
if (getAttribute(self, "alerted-others", false)) return
|
||||
val otherBandits = RegionManager.getLocalNpcs(self, 3).filter { it.id == self.id }
|
||||
for (bandit in otherBandits) {
|
||||
if (!bandit.inCombat())
|
||||
bandit.attack(attacker)
|
||||
}
|
||||
setAttribute(self, "alerted-others", true)
|
||||
}
|
||||
|
||||
override fun onDeathStarted(self: NPC, killer: Entity) {
|
||||
removeAttribute(self, "alerted-others")
|
||||
}
|
||||
}
|
||||
|
|
@ -301,6 +301,9 @@ public abstract class Entity extends Node {
|
|||
* combat zone.
|
||||
*/
|
||||
public boolean isIgnoreMultiBoundaries(Entity victim) {
|
||||
if (this instanceof NPC) {
|
||||
return ((NPC) this).behavior.shouldIgnoreMultiRestrictions((NPC) this, victim);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -337,6 +340,9 @@ public abstract class Entity extends Node {
|
|||
public void onImpact(final Entity entity, BattleState state) {
|
||||
if (DeathTask.isDead(this))
|
||||
state.neutralizeHits();
|
||||
if (this instanceof NPC) {
|
||||
((NPC) this).behavior.afterDamageReceived((NPC) this, entity, state);
|
||||
}
|
||||
if (properties.isRetaliating() && !properties.getCombatPulse().isAttacking() && !getLocks().isInteractionLocked() && properties.getCombatPulse().getNextAttack() < GameWorld.getTicks()) {
|
||||
if (!getWalkingQueue().hasPath() && !getPulseManager().isMovingPulse() || (this instanceof NPC)) {
|
||||
properties.getCombatPulse().attack(entity);
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@ public class NPC extends Entity {
|
|||
*/
|
||||
private String forceTalk;
|
||||
|
||||
public final NPCBehavior behavior;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code NPC} {@code Object}.
|
||||
* @param id The NPC id.
|
||||
|
|
@ -163,6 +165,7 @@ public class NPC extends Entity {
|
|||
super.size = definition.size;
|
||||
super.direction = direction;
|
||||
super.interactPlugin = new InteractPlugin(this);
|
||||
this.behavior = NPCBehavior.forId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -225,6 +228,7 @@ public class NPC extends Entity {
|
|||
npc.size = size;
|
||||
}
|
||||
}
|
||||
behavior.onCreation(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -233,6 +237,7 @@ public class NPC extends Entity {
|
|||
Repository.removeRenderableNPC(this);
|
||||
Repository.getNpcs().remove(this);
|
||||
getViewport().setCurrentPlane(null);
|
||||
behavior.onRemoval(this);
|
||||
// getViewport().setRegion(null);
|
||||
}
|
||||
|
||||
|
|
@ -360,6 +365,7 @@ public class NPC extends Entity {
|
|||
public void checkImpact(BattleState state) {
|
||||
super.checkImpact(state);
|
||||
Entity entity = state.getAttacker();
|
||||
behavior.beforeDamageReceived(this, entity, state);
|
||||
if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) {
|
||||
state.neutralizeHits();
|
||||
}
|
||||
|
|
@ -375,6 +381,8 @@ public class NPC extends Entity {
|
|||
((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster.");
|
||||
}
|
||||
}
|
||||
if (!behavior.canBeAttackedBy(this, entity, style, message))
|
||||
return false;
|
||||
return super.isAttackable(entity, style, message);
|
||||
}
|
||||
|
||||
|
|
@ -395,6 +403,7 @@ public class NPC extends Entity {
|
|||
return;
|
||||
}
|
||||
if (respawnTick == GameWorld.getTicks()) {
|
||||
behavior.onRespawn(this);
|
||||
onRespawn();
|
||||
}
|
||||
handleTickActions();
|
||||
|
|
@ -410,6 +419,8 @@ public class NPC extends Entity {
|
|||
* Handles the automatic actions of the NPC.
|
||||
*/
|
||||
public void handleTickActions() {
|
||||
if (!behavior.tick(this))
|
||||
return;
|
||||
if (!getLocks().isInteractionLocked()) {
|
||||
if (!getLocks().isMovementLocked()) {
|
||||
if (
|
||||
|
|
@ -523,6 +534,12 @@ public class NPC extends Entity {
|
|||
if (!isRespawn())
|
||||
clear();
|
||||
killer.dispatch(new NPCKillEvent(this));
|
||||
behavior.onDeathFinished(this, killer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commenceDeath(Entity killer) {
|
||||
behavior.onDeathStarted(this, killer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -580,7 +597,8 @@ public class NPC extends Entity {
|
|||
|
||||
@Override
|
||||
public CombatSwingHandler getSwingHandler(boolean swing) {
|
||||
return getProperties().getCombatPulse().getStyle().getSwingHandler();
|
||||
CombatSwingHandler original = getProperties().getCombatPulse().getStyle().getSwingHandler();
|
||||
return behavior.getSwingHandlerOverride(this, original);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
122
Server/src/main/core/game/node/entity/npc/NPCBehavior.kt
Normal file
122
Server/src/main/core/game/node/entity/npc/NPCBehavior.kt
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package core.game.node.entity.npc
|
||||
|
||||
import core.api.ContentInterface
|
||||
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.CombatSwingHandler
|
||||
import core.game.node.item.Item
|
||||
|
||||
open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
|
||||
companion object {
|
||||
private val idMap = HashMap<Int,NPCBehavior>()
|
||||
private val defaultBehavior = NPCBehavior()
|
||||
|
||||
@JvmStatic fun forId(id: Int) : NPCBehavior {
|
||||
return idMap[id] ?: defaultBehavior
|
||||
}
|
||||
fun register(ids: IntArray, behavior: NPCBehavior){
|
||||
ids.forEach { idMap[it] = behavior }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every tick, before the base NPC tick() method.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @return whether we should proceed with the base NPC tick() method - e.g. returning false means we do not proceed with a normal NPC tick.
|
||||
*/
|
||||
open fun tick(self: NPC): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before this NPC receives damage, and allows you to adjust the battlestate if needed.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param attacker the entity attacking this NPC
|
||||
* @param state the current state of the combat between this NPC and the attacker.
|
||||
*/
|
||||
open fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {}
|
||||
|
||||
/**
|
||||
* Called after this NPC receives damage, and allows you to adjust the battlestate if needed.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param attacker the entity attacking this NPC
|
||||
* @param state the current state of the combat between this NPC and the attacker.
|
||||
*/
|
||||
open fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) {}
|
||||
|
||||
/**
|
||||
* Called after this NPC's basic attack has been calculated, but before it is finalized, so adjustments can be made.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param victim the entity this NPC is attacking
|
||||
* @param state the state of combat between this NPC and the victim.
|
||||
*/
|
||||
open fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) {}
|
||||
|
||||
/**
|
||||
* Called when this NPC is being removed from the game world.
|
||||
* Note: This is not the same as death. Death does not remove an NPC, unless that NPC cannot respawn.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
*/
|
||||
open fun onRemoval(self: NPC) {}
|
||||
|
||||
/**
|
||||
* Called when this NPC is first created and spawned into the game world.
|
||||
* Note: This is not the same as respawning.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
*/
|
||||
open fun onCreation(self: NPC) {}
|
||||
|
||||
/**
|
||||
* Called when this NPC respawns after being killed.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
*/
|
||||
open fun onRespawn(self: NPC) {}
|
||||
|
||||
/**
|
||||
* Called immediately when the NPC first begins to die (on the same tick that the death animation begins)
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param killer the entity which killed this NPC.
|
||||
*/
|
||||
open fun onDeathStarted(self: NPC, killer: Entity) {}
|
||||
|
||||
/**
|
||||
* Called immediately after the death animation of this NPC has finished, on the same tick that drop tables are rolled.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param killer the entity which killed this NPC.
|
||||
*/
|
||||
open fun onDeathFinished(self: NPC, killer: Entity) {}
|
||||
|
||||
/**
|
||||
* Called after this NPC's drop table is rolled, but before the items are actually dropped, so the list can be manipulated.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param drops the generated list of drops for this roll of the table
|
||||
*/
|
||||
open fun onDropTableRolled(self: NPC, drops: ArrayList<Item>) {}
|
||||
|
||||
/**
|
||||
* Called by combat-related code to check if this NPC can be attacked by the `attacker` entity.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param attacker the entity attempting to attack this NPC
|
||||
* @param style the combat style the attacker is attempting to use
|
||||
* @param shouldSendMessage whether the core combat code believes you should send a message e.g. "You can't attack this NPC with that weapon"
|
||||
* @return whether the attacker should be able to attack this NPC.
|
||||
*/
|
||||
open fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean) : Boolean {return true}
|
||||
|
||||
/**
|
||||
* Called by combat-related code to check if this NPC should ignore multi-combat rules when attempting to attack the given victim.
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param victim the entity that is being considered for attack.
|
||||
* @return whether we should ignore the rules of multi-way combat for the given entity.
|
||||
*/
|
||||
open fun shouldIgnoreMultiRestrictions(self: NPC, victim: Entity) : Boolean {return false}
|
||||
|
||||
/**
|
||||
* Called by combat-related code to allow the combat handler to be overridden
|
||||
* @param self the NPC instance this behavior belongs to
|
||||
* @param original the default swing handler this NPC would have used
|
||||
* @return the SwingHandler instance to be used for this cycle of combat
|
||||
*/
|
||||
open fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler) : CombatSwingHandler {return original}
|
||||
}
|
||||
|
|
@ -74,7 +74,9 @@ public final class NPCDropTables {
|
|||
*/
|
||||
public void drop(NPC npc, Entity looter) {
|
||||
Player p = looter instanceof Player ? (Player) looter : null;
|
||||
table.roll(looter).forEach(item -> createDrop(item,p,npc,npc.getDropLocation()));
|
||||
ArrayList<Item> drops = table.roll(looter);
|
||||
npc.behavior.onDropTableRolled(npc, drops);
|
||||
drops.forEach(item -> createDrop(item,p,npc,npc.getDropLocation()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ public final class MultiwayCombatZone extends MapZone {
|
|||
registerRegion(14939);//Kalphite Stronghold Cave
|
||||
registerRegion(9532); //Isle north of Jatizso
|
||||
registerRegion(10810); //Eastern rock crabs
|
||||
registerRegion(12590); //desert bandits
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.github.classgraph.ScanResult
|
|||
import core.game.bots.PlayerScripts
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.InterfaceListener
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.player.info.login.PlayerSaveParser
|
||||
import core.game.node.entity.player.info.login.PlayerSaver
|
||||
import core.tools.SystemLogger
|
||||
|
|
@ -84,6 +85,7 @@ object ClassScanner {
|
|||
if(clazz is InteractionListener) clazz.defineListeners().also { clazz.defineDestinationOverrides() }
|
||||
if(clazz is InterfaceListener) clazz.defineInterfaceListeners()
|
||||
if(clazz is Commands) clazz.defineCommands()
|
||||
if(clazz is NPCBehavior) NPCBehavior.register(clazz.ids, clazz)
|
||||
if(clazz is PersistPlayer) {
|
||||
PlayerSaver.contentHooks.add(clazz)
|
||||
PlayerSaveParser.contentHooks.add(clazz)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue