607 lines
13 KiB
Java
607 lines
13 KiB
Java
package org.moparscape.msc.gs.model;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.ConcurrentModificationException;
|
|
|
|
import org.moparscape.msc.config.Constants;
|
|
import org.moparscape.msc.config.Formulae;
|
|
import org.moparscape.msc.gs.Instance;
|
|
import org.moparscape.msc.gs.core.GameEngine;
|
|
import org.moparscape.msc.gs.event.DelayedEvent;
|
|
import org.moparscape.msc.gs.event.FightEvent;
|
|
import org.moparscape.msc.gs.model.definition.EntityHandler;
|
|
import org.moparscape.msc.gs.model.definition.entity.ItemDropDef;
|
|
import org.moparscape.msc.gs.model.definition.entity.NPCDef;
|
|
import org.moparscape.msc.gs.model.definition.entity.NPCLoc;
|
|
import org.moparscape.msc.gs.model.landscape.ActiveTile;
|
|
import org.moparscape.msc.gs.plugins.dependencies.NpcAI;
|
|
import org.moparscape.msc.gs.states.Action;
|
|
import org.moparscape.msc.gs.states.CombatState;
|
|
import org.moparscape.msc.gs.tools.DataConversions;
|
|
|
|
public class Npc extends Mob {
|
|
|
|
/**
|
|
* Used for NPC AI scripts.
|
|
*/
|
|
private int stage = 0;
|
|
boolean scripted = false;
|
|
|
|
public int getStage() {
|
|
return stage;
|
|
}
|
|
|
|
public void setStage(int stage) {
|
|
this.stage = stage;
|
|
}
|
|
|
|
public boolean isSpecial() {
|
|
return special;
|
|
}
|
|
|
|
public void setSpecial(boolean special) {
|
|
this.special = special;
|
|
}
|
|
|
|
public int getItemid() {
|
|
return itemid;
|
|
}
|
|
|
|
public void setItemid(int itemid) {
|
|
this.itemid = itemid;
|
|
}
|
|
|
|
public int getExp() {
|
|
return exp;
|
|
}
|
|
|
|
public void setExp(int exp) {
|
|
this.exp = exp;
|
|
}
|
|
|
|
public boolean isHasArmor() {
|
|
return hasArmor;
|
|
}
|
|
|
|
public void setHasArmor(boolean hasArmor) {
|
|
this.hasArmor = hasArmor;
|
|
}
|
|
|
|
public boolean isUndead() {
|
|
return undead;
|
|
}
|
|
|
|
public void setUndead(boolean undead) {
|
|
this.undead = undead;
|
|
}
|
|
|
|
public boolean isRan() {
|
|
return ran;
|
|
}
|
|
|
|
private boolean ran = false;
|
|
/**
|
|
* The identifier for the NPC block event
|
|
*/
|
|
private static final int BLOCKED_IDENTIFIER = 69;
|
|
/**
|
|
* World instance
|
|
*/
|
|
private static final World world = Instance.getWorld();
|
|
/**
|
|
* The player currently blocking this npc
|
|
*/
|
|
private Player blocker = null;
|
|
/**
|
|
* DelayedEvent used for unblocking an npc after set time
|
|
*/
|
|
private DelayedEvent chaseTimeout = null;
|
|
/**
|
|
* Player (if any) that this npc is chasing
|
|
*/
|
|
private Player chasing = null;
|
|
public boolean confused = false;
|
|
/**
|
|
* The npcs hitpoints
|
|
*/
|
|
private int curHits;
|
|
public boolean cursed = false;
|
|
/**
|
|
* The definition of this npc
|
|
*/
|
|
private NPCDef def;
|
|
private Syndicate syndicate = new Syndicate();
|
|
private boolean goingToAttack = false;
|
|
/**
|
|
* The location of this npc
|
|
*/
|
|
private NPCLoc loc;
|
|
|
|
public boolean hasRan() {
|
|
return ran;
|
|
}
|
|
|
|
public void setRan(boolean ran) {
|
|
this.ran = ran;
|
|
}
|
|
|
|
public Player getBlocker() {
|
|
return blocker;
|
|
}
|
|
|
|
public void setBlocker(Player blocker) {
|
|
this.blocker = blocker;
|
|
}
|
|
|
|
public DelayedEvent getChaseTimeout() {
|
|
return chaseTimeout;
|
|
}
|
|
|
|
public void setChaseTimeout(DelayedEvent chaseTimeout) {
|
|
this.chaseTimeout = chaseTimeout;
|
|
}
|
|
|
|
public boolean isConfused() {
|
|
return confused;
|
|
}
|
|
|
|
public void setConfused(boolean confused) {
|
|
this.confused = confused;
|
|
}
|
|
|
|
public int getCurHits() {
|
|
return curHits;
|
|
}
|
|
|
|
public void setCurHits(int curHits) {
|
|
this.curHits = curHits;
|
|
}
|
|
|
|
public boolean isCursed() {
|
|
return cursed;
|
|
}
|
|
|
|
public void setCursed(boolean cursed) {
|
|
this.cursed = cursed;
|
|
}
|
|
|
|
public boolean isGoingToAttack() {
|
|
return goingToAttack;
|
|
}
|
|
|
|
public void setGoingToAttack(boolean goingToAttack) {
|
|
this.goingToAttack = goingToAttack;
|
|
}
|
|
|
|
public boolean isShouldRespawn() {
|
|
return shouldRespawn;
|
|
}
|
|
|
|
public void setShouldRespawn(boolean shouldRespawn) {
|
|
this.shouldRespawn = shouldRespawn;
|
|
}
|
|
|
|
public DelayedEvent getTimeout() {
|
|
return timeout;
|
|
}
|
|
|
|
public void setTimeout(DelayedEvent timeout) {
|
|
this.timeout = timeout;
|
|
}
|
|
|
|
public boolean isWeakend() {
|
|
return weakend;
|
|
}
|
|
|
|
public void setWeakend(boolean weakend) {
|
|
this.weakend = weakend;
|
|
}
|
|
|
|
public void setDef(NPCDef def) {
|
|
this.def = def;
|
|
}
|
|
|
|
public void setLoc(NPCLoc loc) {
|
|
this.loc = loc;
|
|
}
|
|
|
|
/**
|
|
* Should this npc respawn once it has been killed?
|
|
**/
|
|
private boolean shouldRespawn = true;
|
|
/**
|
|
* DelayedEvent used for unblocking an npc after set time
|
|
*/
|
|
private DelayedEvent timeout = null;
|
|
public boolean weakend = false;
|
|
public boolean special = false;
|
|
public int itemid = -1;
|
|
public int exp = -1; // used for events.
|
|
|
|
public Npc(int id, int startX, int startY, int minX, int maxX, int minY,
|
|
int maxY) {
|
|
this(new NPCLoc(id, startX, startY, minX, maxX, minY, maxY));
|
|
}
|
|
|
|
public boolean isScripted() {
|
|
return scripted;
|
|
}
|
|
|
|
public void setScripted(boolean scripted) {
|
|
this.scripted = scripted;
|
|
}
|
|
|
|
public Npc(NPCLoc loc) {
|
|
for (int i : Constants.GameServer.UNDEAD_NPCS) {
|
|
if (loc.getId() == i) {
|
|
this.undead = true;
|
|
}
|
|
}
|
|
for (int i : Constants.GameServer.ARMOR_NPCS) {
|
|
if (loc.getId() == i) {
|
|
this.hasArmor = true;
|
|
}
|
|
}
|
|
|
|
def = EntityHandler.getNpcDef(loc.getId());
|
|
curHits = def.getHits();
|
|
this.loc = loc;
|
|
super.setID(loc.getId());
|
|
super.setLocation(Point.location(loc.startX(), loc.startY()), true);
|
|
super.setCombatLevel(Formulae.getCombatLevel(def.getAtt(),
|
|
def.getDef(), def.getStr(), def.getHits(), 0, 0, 0));
|
|
if (this.loc.getId() == 189 || this.loc.getId() == 53) {
|
|
this.def.aggressive = true;
|
|
}
|
|
for (NpcAI ai : Instance.getPluginHandler().getNpcAI()) {
|
|
if (getID() == ai.getID()) {
|
|
setScripted(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Syndicate getSyndicate() {
|
|
return syndicate;
|
|
}
|
|
|
|
public void setSyndicate(Syndicate syndicate) {
|
|
this.syndicate = syndicate;
|
|
}
|
|
|
|
public synchronized void blockedBy(Player player) {
|
|
blocker = player;
|
|
player.setNpc(this);
|
|
setBusy(true);
|
|
boolean eventExists = false;
|
|
|
|
if (timeout != null) {
|
|
ArrayList<DelayedEvent> events = Instance.getDelayedEventHandler()
|
|
.getEvents();
|
|
|
|
// Damn punk, gettin threading problems here without it synced.
|
|
try {
|
|
synchronized (events) {
|
|
for (DelayedEvent e : events) {
|
|
if (e.is(timeout)) {
|
|
e.updateLastRun(); // If the event still exists,
|
|
// reset its
|
|
// delay time.
|
|
eventExists = true;
|
|
}
|
|
}
|
|
notifyAll();
|
|
}
|
|
} catch (ConcurrentModificationException cme) {
|
|
}
|
|
}
|
|
|
|
if (eventExists) {
|
|
return;
|
|
}
|
|
|
|
timeout = new DelayedEvent(null, 15000) {
|
|
|
|
public Object getIdentifier() {
|
|
return new Integer(BLOCKED_IDENTIFIER);
|
|
}
|
|
|
|
public void run() {
|
|
unblock();
|
|
matchRunning = false;
|
|
}
|
|
};
|
|
|
|
Instance.getDelayedEventHandler().add(timeout);
|
|
}
|
|
|
|
private Player findVictim() {
|
|
if (goingToAttack) {
|
|
return null;
|
|
}
|
|
if (hasRan()) {
|
|
return null;
|
|
}
|
|
long now = GameEngine.getTime();
|
|
if (getChasing() != null) {
|
|
return null;
|
|
}
|
|
ActiveTile[][] tiles = getViewArea().getViewedArea(2, 2, 2, 2);
|
|
for (int x = 0; x < tiles.length; x++) {
|
|
for (int y = 0; y < tiles[x].length; y++) {
|
|
ActiveTile t = tiles[x][y];
|
|
if (t != null) {
|
|
for (Player p : t.getPlayers()) {
|
|
if (!p.isLoggedIn() || p.inCombat()) {
|
|
continue;
|
|
}
|
|
/*
|
|
* if(p.isBusy() || p.isNonaggro() || now -
|
|
* p.getCombatTimer() < (p.getCombatState() ==
|
|
* CombatState.RUNNING || p.getCombatState() ==
|
|
* CombatState.WAITING ? 3000 : 500) ||
|
|
* !p.nextTo(this))) { return p; }
|
|
*/
|
|
|
|
if (p.isBusy()
|
|
|| p.isNonaggro()
|
|
|| now - p.getCombatTimer() < (p
|
|
.getCombatState() == CombatState.RUNNING
|
|
|| p.getCombatState() == CombatState.WAITING ? 3000
|
|
: 500)
|
|
|| !p.nextTo(this)
|
|
|| !p.getLocation().inBounds(loc.minX - 4,
|
|
loc.minY - 4, loc.maxX + 4,
|
|
loc.maxY + 4) || GameEngine.getTime() - p.getLastPing() > 60000) {
|
|
continue;
|
|
}
|
|
|
|
if (getLocation().inWilderness()
|
|
|| p.getCombatLevel() < (getCombatLevel() * 2) + 1) {
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getArmourPoints() {
|
|
return 1;
|
|
}
|
|
|
|
public int getAttack() {
|
|
return def.getAtt();
|
|
}
|
|
|
|
public Player getChasing() {
|
|
return chasing;
|
|
}
|
|
|
|
public int getCombatStyle() {
|
|
return 0;
|
|
}
|
|
|
|
public NPCDef getDef() {
|
|
return EntityHandler.getNpcDef(getID());
|
|
}
|
|
|
|
public int getDefense() {
|
|
return def.getDef();
|
|
}
|
|
|
|
public int getHits() {
|
|
return curHits;
|
|
}
|
|
|
|
public NPCLoc getLoc() {
|
|
return loc;
|
|
}
|
|
|
|
public int getStrength() {
|
|
return def.getStr();
|
|
}
|
|
|
|
public int getWeaponAimPoints() {
|
|
return 1;
|
|
}
|
|
|
|
public int getWeaponPowerPoints() {
|
|
return 1;
|
|
}
|
|
|
|
public void killedBy(Mob mob, boolean stake) {
|
|
if (mob instanceof Player) {
|
|
Player player = (Player) mob;
|
|
player.getActionSender().sendSound("victory");
|
|
if (this.isScripted()) {
|
|
Instance.getPluginHandler().getNpcAIHandler(getID())
|
|
.onNpcDeath(this, player);
|
|
}
|
|
}
|
|
|
|
Mob opponent = super.getOpponent();
|
|
if (opponent != null) {
|
|
opponent.resetCombat(CombatState.WON);
|
|
}
|
|
|
|
resetCombat(CombatState.LOST);
|
|
world.unregisterNpc(this);
|
|
remove();
|
|
|
|
Player owner = mob instanceof Player ? (Player) mob : null;
|
|
|
|
ItemDropDef[] drops = def.getDrops();
|
|
|
|
int total = 0;
|
|
for (ItemDropDef drop : drops) {
|
|
total += drop.getWeight();
|
|
}
|
|
//
|
|
int hit = DataConversions.random(0, total);
|
|
total = 0;
|
|
if (!this.getDef().name.equalsIgnoreCase("ghost")) {
|
|
|
|
|
|
for (ItemDropDef drop : drops) {
|
|
if (drop == null) {
|
|
continue;
|
|
}
|
|
if (drop.getWeight() == 0) {
|
|
world.registerItem(new Item(drop.getID(), getX(),getY(), drop.getAmount(), owner));
|
|
continue;
|
|
}
|
|
|
|
if (hit >= total && hit < (total + drop.getWeight())) {
|
|
if (drop.getID() != -1) {
|
|
if (EntityHandler.getItemDef(drop.getID()).members && World.isMembers()) {
|
|
world.registerItem(new Item(drop.getID(),
|
|
getX(), getY(), drop.getAmount(), owner));
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
total += drop.getWeight();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
World.getQuestManager().handleNpcKilled(this, owner);
|
|
}
|
|
|
|
public void remove() {
|
|
if (!removed && shouldRespawn && def.respawnTime() > 0) {
|
|
Instance.getDelayedEventHandler().add(
|
|
new DelayedEvent(null, def.respawnTime() * 1000) {
|
|
|
|
public void run() {
|
|
world.registerNpc(new Npc(loc));
|
|
matchRunning = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
removed = true;
|
|
|
|
}
|
|
|
|
public void setChasing(Player player) {
|
|
|
|
this.chasing = player;
|
|
goingToAttack = true;
|
|
|
|
if (player == null) {
|
|
this.chasing = null;
|
|
goingToAttack = false;
|
|
return;
|
|
}
|
|
|
|
chaseTimeout = new DelayedEvent(null, 15000) {
|
|
|
|
public void run() {
|
|
|
|
goingToAttack = false;
|
|
setChasing(null);
|
|
matchRunning = false;
|
|
}
|
|
};
|
|
|
|
Instance.getDelayedEventHandler().add(chaseTimeout);
|
|
}
|
|
|
|
public void setHits(int lvl) {
|
|
if (lvl <= 0) {
|
|
lvl = 0;
|
|
}
|
|
|
|
curHits = lvl;
|
|
}
|
|
|
|
public void setRespawn(boolean respawn) {
|
|
shouldRespawn = respawn;
|
|
}
|
|
|
|
public void unblock() {
|
|
if (blocker != null) {
|
|
blocker.setNpc(null);
|
|
blocker = null;
|
|
}
|
|
|
|
if (timeout == null) {
|
|
return;
|
|
}
|
|
|
|
goingToAttack = false;
|
|
setBusy(false);
|
|
timeout.stop();
|
|
timeout = null;
|
|
}
|
|
|
|
public void updatePosition() {
|
|
long now = GameEngine.getTime();
|
|
Player victim = null;
|
|
if (!isBusy() && def.isAggressive() && now - getCombatTimer() > 3000
|
|
&& (victim = findVictim()) != null) {
|
|
resetPath();
|
|
victim.resetPath();
|
|
victim.resetAll();
|
|
if (this.isScripted()) {
|
|
Instance.getPluginHandler().getNpcAIHandler(getID())
|
|
.onNpcAttack(this, victim);
|
|
}
|
|
victim.setStatus(Action.FIGHTING_MOB);
|
|
/*
|
|
* Do not want if (victim.isSleeping()) {
|
|
* victim.getActionSender().sendWakeUp(false); }
|
|
*/
|
|
victim.getActionSender().sendSound("underattack");
|
|
victim.getActionSender().sendMessage("You are under attack!");
|
|
|
|
setLocation(victim.getLocation(), true);
|
|
for (Player p : getViewArea().getPlayersInView()) {
|
|
p.removeWatchedNpc(this);
|
|
}
|
|
|
|
victim.setBusy(true);
|
|
victim.setSprite(9);
|
|
victim.setOpponent(this);
|
|
victim.setCombatTimer();
|
|
|
|
setBusy(true);
|
|
setSprite(8);
|
|
setOpponent(victim);
|
|
setCombatTimer();
|
|
FightEvent fighting = new FightEvent(victim, this, true);
|
|
fighting.setLastRun(0);
|
|
Instance.getDelayedEventHandler().add(fighting);
|
|
}
|
|
|
|
if (now - lastMovement > 2200) {
|
|
if (now - getCombatTimer() < (getCombatState() ==
|
|
CombatState.WAITING ? 5000 : 500)) {
|
|
}
|
|
else {
|
|
lastMovement = now;
|
|
int rand = DataConversions.random(0, 1);
|
|
if (!isBusy() && finishedPath() && rand == 1 && !this.isRemoved()) {
|
|
int newX = DataConversions.random(loc.minX(), loc.maxX());
|
|
int newY = DataConversions.random(loc.minY(), loc.maxY());
|
|
super.setPath(new Path(getX(), getY(), newX, newY));
|
|
}
|
|
}
|
|
}
|
|
|
|
super.updatePosition();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "[NPC:" + EntityHandler.getNpcDef(id).getName() + "]";
|
|
}
|
|
|
|
public boolean hasArmor = false;
|
|
public boolean undead = false;
|
|
}
|