You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2750 lines
85 KiB
2750 lines
85 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.objects; |
|
|
|
import engine.Enum; |
|
import engine.Enum.*; |
|
import engine.InterestManagement.HeightMap; |
|
import engine.InterestManagement.WorldGrid; |
|
import engine.ai.MobileFSM; |
|
import engine.ai.MobileFSM.STATE; |
|
import engine.exception.SerializationException; |
|
import engine.gameManager.*; |
|
import engine.job.JobContainer; |
|
import engine.job.JobScheduler; |
|
import engine.jobs.DeferredPowerJob; |
|
import engine.jobs.UpgradeNPCJob; |
|
import engine.math.Bounds; |
|
import engine.math.Vector3fImmutable; |
|
import engine.net.ByteBufferWriter; |
|
import engine.net.Dispatch; |
|
import engine.net.DispatchMessage; |
|
import engine.net.client.ClientConnection; |
|
import engine.net.client.msg.ErrorPopupMsg; |
|
import engine.net.client.msg.ManageCityAssetsMsg; |
|
import engine.net.client.msg.PetMsg; |
|
import engine.net.client.msg.PlaceAssetMsg; |
|
import engine.net.client.msg.chat.ChatSystemMsg; |
|
import engine.powers.EffectsBase; |
|
import engine.server.MBServerStatics; |
|
import org.joda.time.DateTime; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.util.ArrayList; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.concurrent.ThreadLocalRandom; |
|
import java.util.concurrent.locks.ReentrantReadWriteLock; |
|
|
|
import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; |
|
|
|
public class Mob extends AbstractIntelligenceAgent { |
|
|
|
private static final ReentrantReadWriteLock createLock = new ReentrantReadWriteLock(); |
|
// Variables NOT to be stored in db |
|
private static int staticID = 0; |
|
private static final ConcurrentHashMap<Integer, Mob> mobMapByDBID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); |
|
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); |
|
|
|
//mob specific |
|
private final ConcurrentHashMap<Integer, Boolean> playerAgroMap = new ConcurrentHashMap<>(); |
|
public long nextCastTime = 0; |
|
public long nextCallForHelp = 0; |
|
public ReentrantReadWriteLock minionLock = new ReentrantReadWriteLock(); |
|
public boolean despawned = false; |
|
public Vector3fImmutable destination = Vector3fImmutable.ZERO; |
|
public Vector3fImmutable localLoc = Vector3fImmutable.ZERO; |
|
public HashMap<Integer, Integer> mobPowers; |
|
protected int dbID; //the database ID |
|
protected int loadID; |
|
protected boolean isMob; |
|
public MobBase mobBase; |
|
protected float spawnRadius; |
|
protected int spawnTime; |
|
//used by static mobs |
|
protected int parentZoneID; |
|
protected Zone parentZone; |
|
protected float statLat; |
|
protected float statLon; |
|
protected float statAlt; |
|
protected Building building; |
|
public Contract contract; |
|
private int currentID; |
|
private int ownerUID = 0; //only used by pets |
|
private boolean hasLoot = false; |
|
private AbstractWorldObject fearedObject = null; |
|
private int buildingID; |
|
private boolean isSiege = false; |
|
public boolean isPlayerGuard = false; |
|
private long timeToSpawnSiege; |
|
private AbstractCharacter npcOwner; |
|
private Vector3fImmutable inBuildingLoc = null; |
|
private boolean noAggro = false; |
|
private STATE state = STATE.Disabled; |
|
private int aggroTargetID = 0; |
|
private boolean walkingHome = true; |
|
private long lastAttackTime = 0; |
|
private long deathTime = 0; |
|
private final ConcurrentHashMap<Mob, Integer> siegeMinionMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); |
|
private int patrolPointIndex = 0; |
|
private int lastMobPowerToken = 0; |
|
private HashMap<Integer, MobEquipment> equip = null; |
|
private String nameOverride = ""; |
|
private Regions lastRegion = null; |
|
private long despawnTime = 0; |
|
private DeferredPowerJob weaponPower; |
|
private DateTime upgradeDateTime = null; |
|
private boolean lootSync = false; |
|
private int fidalityID = 0; |
|
private int equipmentSetID = 0; |
|
public int runeSetID = 0; |
|
public int bootySetID = 0; |
|
private int lootSet = 0; |
|
private boolean isGuard; |
|
|
|
/** |
|
* No Id Constructor |
|
*/ |
|
public Mob(String firstName, String lastName, short statStrCurrent, short statDexCurrent, short statConCurrent, |
|
short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, |
|
Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, |
|
byte runningTrains, int npcType, boolean isMob, Zone parent, Building building, int contractID) { |
|
super(firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, sit, |
|
walk, combat, bindLoc, currentLoc, faceDir, healthCurrent, manaCurrent, stamCurrent, guild, runningTrains); |
|
|
|
this.dbID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; |
|
this.state = STATE.Idle; |
|
this.loadID = npcType; |
|
this.isMob = isMob; |
|
this.mobBase = MobBase.getMobBase(loadID); |
|
this.currentID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; |
|
this.parentZone = parent; |
|
this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; |
|
this.building = building; |
|
|
|
if (building != null) |
|
this.buildingID = building.getObjectUUID(); |
|
else this.buildingID = 0; |
|
|
|
if (contractID == 0) |
|
this.contract = null; |
|
else |
|
this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); |
|
|
|
if (this.contract != null) |
|
this.level = 10; |
|
|
|
//initializeMob(false, false); |
|
clearStatic(); |
|
} |
|
|
|
/** |
|
* Normal Constructor |
|
*/ |
|
public Mob(String firstName, String lastName, short statStrCurrent, short statDexCurrent, short statConCurrent, |
|
short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, |
|
Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, |
|
byte runningTrains, int npcType, boolean isMob, Zone parent, int newUUID, Building building, int contractID) { |
|
super(firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, sit, |
|
walk, combat, bindLoc, currentLoc, faceDir, healthCurrent, manaCurrent, stamCurrent, guild, runningTrains, newUUID); |
|
this.state = STATE.Idle; |
|
this.dbID = newUUID; |
|
this.loadID = npcType; |
|
this.isMob = isMob; |
|
|
|
if (contractID == 0) |
|
this.contract = null; |
|
else |
|
this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); |
|
|
|
this.mobBase = MobBase.getMobBase(loadID); |
|
this.parentZone = parent; |
|
this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; |
|
this.building = building; |
|
initializeMob(false, false, false); |
|
clearStatic(); |
|
} |
|
|
|
/** |
|
* Pet Constructor |
|
*/ |
|
public Mob(MobBase mobBase, Guild guild, Zone parent, short level, PlayerCharacter owner, int tableID) { |
|
super(mobBase.getFirstName(), "", (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, level, 0, false, true, false, owner.getLoc(), owner.getLoc(), owner.getFaceDir(), (short) mobBase.getHealthMax(), (short) 0, (short) 0, guild, (byte) 0, tableID); |
|
this.state = STATE.Idle; |
|
this.dbID = tableID; |
|
this.loadID = mobBase.getObjectUUID(); |
|
this.isMob = true; |
|
this.mobBase = mobBase; |
|
this.parentZone = parent; |
|
this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; |
|
this.ownerUID = owner.getObjectUUID(); |
|
initializeMob(true, false, false); |
|
clearStatic(); |
|
} |
|
|
|
//SIEGE CONSTRUCTOR |
|
public Mob(MobBase mobBase, Guild guild, Zone parent, short level, Vector3fImmutable loc, int tableID, boolean isPlayerGuard) { |
|
super(mobBase.getFirstName(), "", (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, level, 0, false, true, false, loc, loc, Vector3fImmutable.ZERO, (short) mobBase.getHealthMax(), (short) 0, (short) 0, guild, (byte) 0, tableID); |
|
this.dbID = tableID; |
|
this.loadID = mobBase.getObjectUUID(); |
|
this.isMob = true; |
|
this.mobBase = mobBase; |
|
this.parentZone = parent; |
|
this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; |
|
this.ownerUID = 0; |
|
this.equip = new HashMap<>(); |
|
initializeMob(false, true, isPlayerGuard); |
|
clearStatic(); |
|
} |
|
|
|
/** |
|
* ResultSet Constructor |
|
*/ |
|
public Mob(ResultSet rs) throws SQLException { |
|
|
|
super(rs); |
|
|
|
try { |
|
this.dbID = rs.getInt(1); |
|
this.state = STATE.Idle; |
|
this.loadID = rs.getInt("mob_mobbaseID"); |
|
this.gridObjectType = GridObjectType.DYNAMIC; |
|
this.spawnRadius = rs.getFloat("mob_spawnRadius"); |
|
this.spawnTime = rs.getInt("mob_spawnTime"); |
|
this.isMob = true; |
|
this.parentZone = null; |
|
this.statLat = rs.getFloat("mob_spawnX"); |
|
this.statAlt = rs.getFloat("mob_spawnY"); |
|
this.statLon = rs.getFloat("mob_spawnZ"); |
|
|
|
this.localLoc = new Vector3fImmutable(this.statLat, this.statAlt, this.statLon); |
|
|
|
this.parentZoneID = rs.getInt("parent"); |
|
this.level = (short) rs.getInt("mob_level"); |
|
int buildingID = rs.getInt("mob_buildingID"); |
|
|
|
try { |
|
this.building = BuildingManager.getBuilding(buildingID); |
|
} catch (Exception e) { |
|
this.building = null; |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
int contractID = rs.getInt("mob_contractID"); |
|
|
|
if (contractID == 0) |
|
this.contract = null; |
|
else |
|
this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); |
|
|
|
if (this.contract != null) |
|
if (NPC.ISGuardCaptain(contract.getContractID())) { |
|
this.spawnTime = 60 * 15; |
|
this.isPlayerGuard = true; |
|
this.nameOverride = contract.getName(); |
|
} |
|
|
|
int guildID = rs.getInt("mob_guildUID"); |
|
|
|
|
|
if (this.fidalityID != 0) { |
|
if (this.building != null) |
|
this.guild = this.building.getGuild(); |
|
else |
|
this.guild = Guild.getGuild(guildID); |
|
} else if (this.building != null) |
|
this.guild = this.building.getGuild(); |
|
else |
|
this.guild = Guild.getGuild(guildID); |
|
|
|
if (this.guild == null) |
|
this.guild = Guild.getErrantGuild(); |
|
|
|
java.util.Date sqlDateTime; |
|
sqlDateTime = rs.getTimestamp("upgradeDate"); |
|
|
|
if (sqlDateTime != null) |
|
upgradeDateTime = new DateTime(sqlDateTime); |
|
else |
|
upgradeDateTime = null; |
|
|
|
// Submit upgrade job if NPC is currently set to rank. |
|
|
|
if (this.upgradeDateTime != null) |
|
Mob.submitUpgradeJob(this); |
|
|
|
this.mobBase = MobBase.getMobBase(loadID); |
|
|
|
this.setObjectTypeMask(MBServerStatics.MASK_MOB | this.getTypeMasks()); |
|
|
|
if (this.mobBase != null && this.spawnTime == 0) |
|
this.spawnTime = this.mobBase.getSpawnTime(); |
|
|
|
this.bindLoc = new Vector3fImmutable(this.statLat, this.statAlt, this.statLon); |
|
|
|
this.setParentZone(ZoneManager.getZoneByUUID(this.parentZoneID)); |
|
|
|
|
|
this.fidalityID = rs.getInt("fidalityID"); |
|
|
|
this.equipmentSetID = rs.getInt("equipmentSet"); |
|
this.runeSetID = rs.getInt("runeSet"); |
|
this.bootySetID = rs.getInt("bootySet"); |
|
|
|
if (this.contract != null) |
|
this.equipmentSetID = this.contract.getEquipmentSet(); |
|
|
|
this.lootSet = (rs.getInt("lootSet")); |
|
|
|
if (this.fidalityID != 0) |
|
this.nameOverride = rs.getString("mob_name"); |
|
|
|
} catch (Exception e) { |
|
Logger.error(currentID + ""); |
|
} |
|
|
|
try { |
|
initializeMob(false, false, this.isPlayerGuard); |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} |
|
|
|
} |
|
|
|
public static void __serializeForClientMsg(Mob mob, ByteBufferWriter writer) throws SerializationException { |
|
} |
|
|
|
public static void serializeMobForClientMsgOtherPlayer(Mob mob, ByteBufferWriter writer, boolean hideAsciiLastName) |
|
throws SerializationException { |
|
Mob.serializeForClientMsgOtherPlayer(mob, writer); |
|
} |
|
|
|
public static void serializeForClientMsgOtherPlayer(Mob mob, ByteBufferWriter writer) |
|
throws SerializationException { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
int tid = (mob.mobBase != null) ? mob.mobBase.getLoadID() : 0; |
|
int classID = MobBase.GetClassType(mob.mobBase.getObjectUUID()); |
|
if (mob.isPet()) { |
|
writer.putInt(2); |
|
writer.putInt(3); |
|
writer.putInt(0); |
|
writer.putInt(2522); |
|
writer.putInt(GameObjectType.NPCClassRune.ordinal()); |
|
writer.putInt(mob.currentID); |
|
} else if (tid == 100570) { //kur'adar |
|
writer.putInt(3); |
|
Mob.serializeRune(mob, writer, 3, GameObjectType.NPCClassRuneTwo.ordinal(), 2518); //warrior class |
|
serializeRune(mob, writer, 5, GameObjectType.NPCClassRuneThree.ordinal(), 252621); //guard rune |
|
} else if (tid == 100962 || tid == 100965) { //Spydraxxx the Mighty, Denigo Tantric |
|
writer.putInt(2); |
|
serializeRune(mob, writer, 5, GameObjectType.NPCClassRuneTwo.ordinal(), 252621); //guard rune |
|
} else if (mob.contract != null || mob.isPlayerGuard) { |
|
writer.putInt(3); |
|
serializeRune(mob, writer, 3, GameObjectType.NPCClassRuneTwo.ordinal(), MobBase.GetClassType(mob.getMobBaseID())); //warrior class |
|
serializeRune(mob, writer, 5, GameObjectType.NPCClassRuneThree.ordinal(), 252621); //guard rune |
|
} else { |
|
|
|
writer.putInt(1); |
|
} |
|
|
|
//Generate Race Rune |
|
writer.putInt(1); |
|
writer.putInt(0); |
|
|
|
if (mob.mobBase != null) |
|
writer.putInt(mob.mobBase.getLoadID()); |
|
else |
|
writer.putInt(mob.loadID); |
|
|
|
writer.putInt(mob.getObjectType().ordinal()); |
|
writer.putInt(mob.currentID); |
|
|
|
//Send Stats |
|
writer.putInt(5); |
|
writer.putInt(0x8AC3C0E6); //Str |
|
writer.putInt(0); |
|
writer.putInt(0xACB82E33); //Dex |
|
writer.putInt(0); |
|
writer.putInt(0xB15DC77E); //Con |
|
writer.putInt(0); |
|
writer.putInt(0xE07B3336); //Int |
|
writer.putInt(0); |
|
writer.putInt(0xFF665EC3); //Spi |
|
writer.putInt(0); |
|
|
|
if (!mob.nameOverride.isEmpty()) { |
|
writer.putString(mob.nameOverride); |
|
writer.putInt(0); |
|
} else { |
|
writer.putString(mob.firstName); |
|
writer.putString(mob.lastName); |
|
|
|
} |
|
|
|
|
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
writer.put((byte) 0); |
|
writer.putInt(mob.getObjectType().ordinal()); |
|
writer.putInt(mob.currentID); |
|
|
|
if (mob.mobBase != null) { |
|
writer.putFloat(mob.mobBase.getScale()); |
|
writer.putFloat(mob.mobBase.getScale()); |
|
writer.putFloat(mob.mobBase.getScale()); |
|
} else { |
|
writer.putFloat(1.0f); |
|
writer.putFloat(1.0f); |
|
writer.putFloat(1.0f); |
|
} |
|
|
|
//Believe this is spawn loc, ignore for now |
|
writer.putVector3f(mob.getLoc()); |
|
|
|
//Rotation |
|
writer.putFloat(mob.getRot().y); |
|
|
|
//Inventory Stuff |
|
writer.putInt(0); |
|
|
|
// get a copy of the equipped items. |
|
|
|
|
|
if (mob.equip != null) { |
|
|
|
writer.putInt(mob.equip.size()); |
|
|
|
for (MobEquipment me : mob.equip.values()) { |
|
MobEquipment.serializeForClientMsg(me, writer); |
|
} |
|
} else { |
|
writer.putInt(0); |
|
} |
|
|
|
writer.putInt(mob.getRank()); |
|
writer.putInt(mob.getLevel()); |
|
writer.putInt(mob.getIsSittingAsInt()); //Standing |
|
writer.putInt(mob.getIsWalkingAsInt()); //Walking |
|
writer.putInt(mob.getIsCombatAsInt()); //Combat |
|
writer.putInt(2); //Unknown |
|
writer.putInt(1); //Unknown - Headlights? |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
writer.putInt(0); |
|
|
|
if (mob.contract != null && mob.npcOwner == null) { |
|
writer.put((byte) 1); |
|
writer.putLong(0); |
|
writer.putLong(0); |
|
|
|
if (mob.contract != null) |
|
writer.putInt(mob.contract.getIconID()); |
|
else |
|
writer.putInt(0); //npc icon ID |
|
|
|
} else |
|
writer.put((byte) 0); |
|
|
|
|
|
if (mob.npcOwner != null) { |
|
writer.put((byte) 1); |
|
writer.putInt(GameObjectType.PlayerCharacter.ordinal()); |
|
writer.putInt(131117009); |
|
writer.putInt(mob.npcOwner.getObjectType().ordinal()); |
|
writer.putInt(mob.npcOwner.getObjectUUID()); |
|
writer.putInt(8); |
|
} else |
|
writer.put((byte) 0); |
|
|
|
if (mob.isPet()) { |
|
|
|
writer.put((byte) 1); |
|
|
|
if (mob.getOwner() != null) { |
|
writer.putInt(mob.getOwner().getObjectType().ordinal()); |
|
writer.putInt(mob.getOwner().getObjectUUID()); |
|
} else { |
|
writer.putInt(0); //ownerType |
|
writer.putInt(0); //ownerID |
|
} |
|
} else { |
|
writer.put((byte) 0); |
|
} |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
if (!mob.isAlive() && !mob.isPet() && !mob.isNecroPet() && !mob.isSiege && !mob.isPlayerGuard) { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
} |
|
|
|
writer.put((byte) 0); |
|
Guild._serializeForClientMsg(mob.getGuild(), writer); |
|
// writer.putInt(0); |
|
// writer.putInt(0); |
|
if (mob.mobBase != null && mob.mobBase.getObjectUUID() == 100570) { |
|
writer.putInt(2); |
|
writer.putInt(0x00008A2E); |
|
writer.putInt(0x1AB84003); |
|
} else if (mob.isSiege) { |
|
writer.putInt(1); |
|
writer.putInt(74620179); |
|
} else |
|
writer.putInt(0); |
|
|
|
// writer.putInt(1); |
|
// writer.putInt(0); //0xAC13C5E9 - alternate textures |
|
writer.putInt(0); //0xB8400300 |
|
writer.putInt(0); |
|
|
|
//TODO Guard |
|
writer.put((byte) 0); |
|
// writer.put((byte)0); //Is guard.. |
|
|
|
writer.putFloat(mob.healthMax); |
|
writer.putFloat(mob.health.get()); |
|
|
|
//TODO Peace Zone |
|
writer.put((byte) 1); //0=show tags, 1=don't |
|
|
|
//DON't LOAD EFFECTS FOR DEAD MOBS. |
|
|
|
if (!mob.isAlive()) |
|
writer.putInt(0); |
|
else { |
|
int indexPosition = writer.position(); |
|
writer.putInt(0); //placeholder for item cnt |
|
int total = 0; |
|
|
|
// Logger.info("",""+ mob.getEffects().size()); |
|
for (Effect eff : mob.getEffects().values()) { |
|
if (eff.isStatic()) |
|
continue; |
|
if (!eff.serializeForLoad(writer)) |
|
continue; |
|
++total; |
|
} |
|
|
|
writer.putIntAt(total, indexPosition); |
|
} |
|
|
|
// // Effects |
|
writer.put((byte) 0); |
|
} |
|
|
|
private static void serializeRune(Mob mob, ByteBufferWriter writer, int type, int objectType, int runeID) { |
|
writer.putInt(type); |
|
writer.putInt(0); |
|
writer.putInt(runeID); |
|
writer.putInt(objectType); |
|
writer.putInt(mob.currentID); |
|
} |
|
|
|
/** |
|
* Generate a random quantity of gold for this mob. |
|
* |
|
* @return Quantity of gold |
|
*/ |
|
public static int randomGoldAmount(Mob mob) { |
|
|
|
// percentage chance to drop gold |
|
|
|
//R8 mobs have 100% gold drop. |
|
if (mob.getLevel() < 80) |
|
if ((ThreadLocalRandom.current().nextDouble() * 100d) > MBServerStatics.GOLD_DROP_PERCENTAGE_CHANCE) |
|
return 0; |
|
|
|
|
|
int level = mob.getLevel(); |
|
level = Math.max(level, 0); |
|
level = Math.min(level, 50); |
|
|
|
double minGold; |
|
double maxGold; |
|
|
|
if (mob.mobBase != null) { |
|
minGold = mob.mobBase.getMinGold(); |
|
maxGold = mob.mobBase.getMaxGold(); |
|
} else { |
|
minGold = MBServerStatics.GOLD_DROP_MINIMUM_PER_MOB_LEVEL[level]; |
|
maxGold = MBServerStatics.GOLD_DROP_MAXIMUM_PER_MOB_LEVEL[level]; |
|
} |
|
|
|
double gold = (ThreadLocalRandom.current().nextDouble() * (maxGold - minGold) + minGold); |
|
|
|
//server specific gold multiplier |
|
|
|
gold *= Float.parseFloat(ConfigManager.MB_NORMAL_DROP_RATE.getValue()); |
|
|
|
//modify for hotzone |
|
|
|
if (ZoneManager.inHotZone(mob.getLoc())) |
|
gold *= Float.parseFloat(ConfigManager.MB_HOTZONE_DROP_RATE.getValue()); |
|
|
|
return (int) gold; |
|
} |
|
|
|
public static Mob createMob(int loadID, Vector3fImmutable spawn, Guild guild, boolean isMob, Zone parent, Building building, int contractID) { |
|
|
|
Mob mobWithoutID = new Mob("", "", (short) 0, (short) 0, (short) 0, (short) 0, |
|
(short) 0, (short) 1, 0, false, false, false, spawn, spawn, Vector3fImmutable.ZERO, |
|
(short) 1, (short) 1, (short) 1, guild, (byte) 0, loadID, isMob, parent, building, contractID); |
|
|
|
if (parent != null) { |
|
mobWithoutID.setRelPos(parent, spawn.x - parent.absX, spawn.y - parent.absY, spawn.z - parent.absZ); |
|
} |
|
|
|
|
|
if (mobWithoutID.mobBase == null) { |
|
return null; |
|
} |
|
Mob mob; |
|
try { |
|
mob = DbManager.MobQueries.ADD_MOB(mobWithoutID, isMob); |
|
mob.setObjectTypeMask(MBServerStatics.MASK_MOB | mob.getTypeMasks()); |
|
mob.setMob(); |
|
mob.setParentZone(parent); |
|
} catch (Exception e) { |
|
Logger.error("SQLException:" + e.getMessage()); |
|
mob = null; |
|
} |
|
return mob; |
|
} |
|
|
|
public static Mob createPet(int loadID, Guild guild, Zone parent, PlayerCharacter owner, short level) { |
|
MobBase mobBase = MobBase.getMobBase(loadID); |
|
Mob mob = null; |
|
if (mobBase == null || owner == null) { |
|
return null; |
|
} |
|
createLock.writeLock().lock(); |
|
level += 20; |
|
try { |
|
mob = new Mob(mobBase, guild, parent, level, owner, 0); |
|
if (mob.mobBase == null) { |
|
return null; |
|
} |
|
mob.runAfterLoad(); |
|
|
|
Vector3fImmutable loc = owner.getLoc(); |
|
if (parent != null) { |
|
mob.setRelPos(parent, loc.x - parent.absX, loc.y - parent.absY, loc.z - parent.absZ); |
|
} |
|
DbManager.addToCache(mob); |
|
mob.setPet(owner, true); |
|
mob.setWalkMode(false); |
|
mob.state = STATE.Awake; |
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} finally { |
|
createLock.writeLock().unlock(); |
|
} |
|
|
|
return mob; |
|
} |
|
|
|
public static int nextStaticID() { |
|
int id = Mob.staticID; |
|
Mob.staticID++; |
|
return id; |
|
} |
|
|
|
public static Mob getMob(int id) { |
|
|
|
if (id == 0) |
|
return null; |
|
|
|
Mob mob = (Mob) DbManager.getFromCache(GameObjectType.Mob, id); |
|
if (mob != null) |
|
return mob; |
|
return DbManager.MobQueries.GET_MOB(id); |
|
} |
|
|
|
public static Mob getFromCache(int id) { |
|
|
|
|
|
return (Mob) DbManager.getFromCache(GameObjectType.Mob, id); |
|
} |
|
|
|
public static Mob getFromCacheDBID(int id) { |
|
if (Mob.mobMapByDBID.containsKey(id)) { |
|
return Mob.mobMapByDBID.get(id); |
|
} |
|
return null; |
|
} |
|
|
|
private static float getModifiedAmount(CharacterSkill skill) { |
|
|
|
if (skill == null) |
|
return 0f; |
|
|
|
return skill.getModifiedAmount(); |
|
} |
|
|
|
public static void HandleAssistedAggro(PlayerCharacter source, PlayerCharacter target) { |
|
|
|
HashSet<AbstractWorldObject> mobsInRange = WorldGrid.getObjectsInRangePartial(source, MBServerStatics.AI_DROP_AGGRO_RANGE, MBServerStatics.MASK_MOB); |
|
|
|
for (AbstractWorldObject awo : mobsInRange) { |
|
Mob mob = (Mob) awo; |
|
|
|
//Mob is not attacking anyone, skip. |
|
if (mob.getCombatTarget() == null) |
|
continue; |
|
|
|
//Mob not attacking target's target, let's not be failmu and skip this target. |
|
if (mob.getCombatTarget() != target) |
|
continue; |
|
|
|
//target is mob's combat target, LETS GO. |
|
if (source.getHateValue() > target.getHateValue()) { |
|
mob.setCombatTarget(source); |
|
MobileFSM.setAggro(mob, source.getObjectUUID()); |
|
} |
|
} |
|
} |
|
|
|
public static void submitUpgradeJob(Mob mob) { |
|
|
|
JobContainer jc; |
|
|
|
if (mob.getUpgradeDateTime() == null) { |
|
Logger.error("Failed to get Upgrade Date"); |
|
return; |
|
} |
|
|
|
// Submit upgrade job for future date or current instant |
|
|
|
if (mob.getUpgradeDateTime().isAfter(DateTime.now())) |
|
jc = JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(mob), |
|
mob.getUpgradeDateTime().getMillis()); |
|
else |
|
JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(mob), 0); |
|
|
|
} |
|
|
|
public static int getUpgradeTime(Mob mob) { |
|
|
|
if (mob.getRank() < 7) |
|
return (mob.getRank() * 8); |
|
|
|
return 0; |
|
} |
|
|
|
public static int getUpgradeCost(Mob mob) { |
|
|
|
int upgradeCost; |
|
|
|
upgradeCost = Integer.MAX_VALUE; |
|
|
|
if (mob.getRank() < 7) |
|
return (mob.getRank() * 100650) + 21450; |
|
|
|
return upgradeCost; |
|
} |
|
|
|
public static void setUpgradeDateTime(Mob mob, DateTime upgradeDateTime) { |
|
|
|
if (!DbManager.MobQueries.updateUpgradeTime(mob, upgradeDateTime)) { |
|
Logger.error("Failed to set upgradeTime for building " + mob.currentID); |
|
return; |
|
} |
|
mob.upgradeDateTime = upgradeDateTime; |
|
} |
|
|
|
public static Vector3fImmutable GetSpawnRadiusLocation(Mob mob) { |
|
|
|
Vector3fImmutable returnLoc = Vector3fImmutable.ZERO; |
|
|
|
if (mob.fidalityID != 0 && mob.building != null) { |
|
|
|
|
|
Vector3fImmutable spawnRadiusLoc = Vector3fImmutable.getRandomPointInCircle(mob.localLoc, mob.spawnRadius); |
|
|
|
Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(mob.building, spawnRadiusLoc); |
|
|
|
return buildingWorldLoc; |
|
|
|
|
|
} else { |
|
|
|
boolean run = true; |
|
|
|
while (run) { |
|
Vector3fImmutable localLoc = new Vector3fImmutable(mob.statLat + mob.parentZone.absX, mob.statAlt + mob.parentZone.absY, mob.statLon + mob.parentZone.absZ); |
|
Vector3fImmutable spawnRadiusLoc = Vector3fImmutable.getRandomPointInCircle(localLoc, mob.spawnRadius); |
|
|
|
//not a roaming mob, just return the random loc. |
|
if (mob.spawnRadius < 12000) |
|
return spawnRadiusLoc; |
|
|
|
Zone spawnZone = ZoneManager.findSmallestZone(spawnRadiusLoc); |
|
//dont spawn roaming mobs in npc cities |
|
if (spawnZone.isNPCCity()) |
|
continue; |
|
|
|
//dont spawn roaming mobs in player cities. |
|
if (spawnZone.isPlayerCity()) |
|
continue; |
|
|
|
//don't spawn mobs in water. |
|
if (HeightMap.isLocUnderwater(spawnRadiusLoc)) |
|
continue; |
|
|
|
run = false; |
|
|
|
return spawnRadiusLoc; |
|
|
|
} |
|
|
|
} |
|
|
|
//shouldn't ever get here. |
|
|
|
return returnLoc; |
|
} |
|
|
|
private void clearStatic() { |
|
|
|
if (this.parentZone != null) |
|
this.parentZone.zoneMobSet.remove(this); |
|
|
|
this.parentZone = null; |
|
this.statLat = 0f; |
|
this.statLon = 0f; |
|
this.statAlt = 0f; |
|
} |
|
|
|
private void initializeMob(boolean isPet, boolean isSiege, boolean isGuard) { |
|
|
|
if (this.mobBase != null) { |
|
|
|
this.gridObjectType = GridObjectType.DYNAMIC; |
|
this.healthMax = this.mobBase.getHealthMax(); |
|
this.manaMax = 0; |
|
this.staminaMax = 0; |
|
this.setHealth(this.healthMax); |
|
this.mana.set(this.manaMax); |
|
this.stamina.set(this.staminaMax); |
|
|
|
if (!this.nameOverride.isEmpty()) |
|
this.firstName = this.nameOverride; |
|
else |
|
this.firstName = this.mobBase.getFirstName(); |
|
if (isPet) { |
|
this.setObjectTypeMask(MBServerStatics.MASK_PET | this.getTypeMasks()); |
|
if (ConfigManager.serverType.equals(ServerType.LOGINSERVER)) |
|
this.setLoc(this.getLoc()); |
|
} |
|
if (!isPet && this.contract == null) { |
|
this.level = (short) this.mobBase.getLevel(); |
|
} |
|
|
|
} else |
|
this.level = 1; |
|
|
|
//add this npc to building |
|
if (this.building != null && this.loadID != 0 && this.fidalityID == 0) { |
|
|
|
int maxSlots; |
|
maxSlots = building.getBlueprint().getSlotsForRank(this.building.getRank()); |
|
|
|
for (int slot = 1; slot < maxSlots + 1; slot++) { |
|
if (!this.building.getHirelings().containsValue(slot)) { |
|
this.building.getHirelings().put(this, slot); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//set bonuses |
|
this.bonuses = new PlayerBonuses(this); |
|
|
|
//TODO set these correctly later |
|
this.rangeHandOne = 8; |
|
this.rangeHandTwo = -1; |
|
this.minDamageHandOne = 0; |
|
this.maxDamageHandOne = 0; |
|
this.minDamageHandTwo = 1; |
|
this.maxDamageHandTwo = 4; |
|
this.atrHandOne = 300; |
|
this.atrHandOne = 300; |
|
this.defenseRating = (short) this.mobBase.getDefenseRating(); |
|
this.isActive = true; |
|
|
|
this.charItemManager.load(); |
|
|
|
//load AI for general mobs. |
|
|
|
if (isPet || isSiege || (isGuard && this.contract == null)) |
|
this.currentID = (--Mob.staticID); |
|
else |
|
this.currentID = this.dbID; |
|
|
|
if (!isPet && !isSiege && !this.isPlayerGuard) |
|
loadInventory(); |
|
|
|
//store mobs by Database ID |
|
|
|
if (!isPet && !isSiege) |
|
Mob.mobMapByDBID.put(this.dbID, this); |
|
} |
|
|
|
/* |
|
* Getters |
|
*/ |
|
@Override |
|
public int getDBID() { |
|
return this.dbID; |
|
} |
|
|
|
public int getLoadID() { |
|
return loadID; |
|
} |
|
|
|
@Override |
|
public int getObjectUUID() { |
|
return currentID; |
|
} |
|
|
|
public float getSpawnX() { |
|
return this.statLat; |
|
} |
|
|
|
/* |
|
* Serialization |
|
*/ |
|
|
|
public float getSpawnY() { |
|
return this.statAlt; |
|
} |
|
|
|
public float getSpawnZ() { |
|
return this.statLon; |
|
} |
|
|
|
public float getSpawnRadius() { |
|
return this.spawnRadius; |
|
} |
|
|
|
public int getSpawnTime() { |
|
|
|
if (this.spawnTime == 0) |
|
return MBServerStatics.RESPAWN_TIMER; |
|
else |
|
return this.spawnTime * 1000; |
|
} |
|
|
|
public void setSpawnTime(int value) { |
|
this.spawnTime = value; |
|
} |
|
|
|
//use getSpawnTime instead. This is just for init tables |
|
public int getTrueSpawnTime() { |
|
return this.spawnTime; |
|
} |
|
|
|
public String getSpawnTimeAsString() { |
|
if (this.spawnTime == 0) |
|
return MBServerStatics.DEFAULT_SPAWN_TIME_MS / 1000 + " seconds (Default)"; |
|
else |
|
return this.spawnTime + " seconds"; |
|
|
|
} |
|
|
|
@Override |
|
public MobBase getMobBase() { |
|
return this.mobBase; |
|
} |
|
|
|
public int getMobBaseID() { |
|
|
|
if (this.mobBase != null) |
|
return this.mobBase.getObjectUUID(); |
|
|
|
return 0; |
|
} |
|
|
|
public Vector3fImmutable getTrueBindLoc() { |
|
return this.bindLoc; |
|
} |
|
|
|
public Zone getParentZone() { |
|
return this.parentZone; |
|
} |
|
|
|
public void setParentZone(Zone zone) { |
|
|
|
if (this.parentZone == null) { |
|
zone.zoneMobSet.add(this); |
|
this.parentZone = zone; |
|
} |
|
|
|
this.bindLoc = Mob.GetSpawnRadiusLocation(this); |
|
this.lastBindLoc = bindLoc; |
|
this.setLoc(bindLoc); |
|
this.stopMovement(bindLoc); |
|
} |
|
|
|
public int getParentZoneID() { |
|
|
|
if (this.parentZone != null) |
|
return this.parentZone.getObjectUUID(); |
|
|
|
return 0; |
|
} |
|
|
|
@Override |
|
public int getGuildUUID() { |
|
|
|
if (this.guild == null) |
|
return 0; |
|
|
|
return this.guild.getObjectUUID(); |
|
} |
|
|
|
@Override |
|
public PlayerCharacter getOwner() { |
|
|
|
if (!this.isPet()) |
|
return null; |
|
|
|
if (this.ownerUID == 0) |
|
return null; |
|
|
|
return PlayerCharacter.getFromCache(this.ownerUID); |
|
} |
|
|
|
public void setOwner(PlayerCharacter value) { |
|
|
|
if (value == null) |
|
this.ownerUID = 0; |
|
else |
|
this.ownerUID = value.getObjectUUID(); |
|
} |
|
|
|
@Override |
|
public AbstractWorldObject getFearedObject() { |
|
return this.fearedObject; |
|
} |
|
|
|
public void setFearedObject(AbstractWorldObject awo) { |
|
this.fearedObject = awo; |
|
} |
|
|
|
@Override |
|
public Vector3fImmutable getBindLoc() { |
|
|
|
if (this.isPet() && !this.isSiege) |
|
return this.getOwner() != null ? this.getOwner().getLoc() : this.getLoc(); |
|
return this.bindLoc; |
|
} |
|
|
|
public void calculateModifiedStats() { |
|
|
|
float strVal = this.mobBase.getMobBaseStats().getBaseStr(); |
|
float dexVal = this.mobBase.getMobBaseStats().getBaseDex(); |
|
float conVal = 0; // I believe this will desync the Mobs Health if we call it. |
|
float intVal = this.mobBase.getMobBaseStats().getBaseInt(); |
|
float spiVal = this.mobBase.getMobBaseStats().getBaseSpi(); |
|
|
|
// TODO modify for equipment |
|
if (this.bonuses != null) { |
|
// modify for effects |
|
strVal += this.bonuses.getFloat(ModType.Attr, SourceType.Strength); |
|
dexVal += this.bonuses.getFloat(ModType.Attr, SourceType.Dexterity); |
|
conVal += this.bonuses.getFloat(ModType.Attr, SourceType.Constitution); |
|
intVal += this.bonuses.getFloat(ModType.Attr, SourceType.Intelligence); |
|
spiVal += this.bonuses.getFloat(ModType.Attr, SourceType.Spirit); |
|
|
|
// apply dex penalty for armor |
|
// modify percent amounts. DO THIS LAST! |
|
strVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Strength)); |
|
dexVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Dexterity)); |
|
conVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Constitution)); |
|
intVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Intelligence)); |
|
spiVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Spirit)); |
|
} else { |
|
// apply dex penalty for armor |
|
} |
|
|
|
// Set current stats |
|
this.statStrCurrent = (strVal < 1) ? (short) 1 : (short) strVal; |
|
this.statDexCurrent = (dexVal < 1) ? (short) 1 : (short) dexVal; |
|
this.statConCurrent = (conVal < 1) ? (short) 1 : (short) conVal; |
|
this.statIntCurrent = (intVal < 1) ? (short) 1 : (short) intVal; |
|
this.statSpiCurrent = (spiVal < 1) ? (short) 1 : (short) spiVal; |
|
|
|
} |
|
|
|
@Override |
|
public float getSpeed() { |
|
float bonus = 1; |
|
if (this.bonuses != null) |
|
// get rune and effect bonuses |
|
bonus *= (1 + this.bonuses.getFloatPercentAll(ModType.Speed, SourceType.None)); |
|
|
|
if (this.isPlayerGuard) { |
|
switch (this.mobBase.getLoadID()) { |
|
case 2111: |
|
if (this.isWalk()) |
|
if (this.isCombat()) |
|
return Enum.Guards.HumanArcher.getWalkCombatSpeed() * bonus; |
|
else return Enum.Guards.HumanArcher.getWalkSpeed() * bonus; |
|
else |
|
return Enum.Guards.HumanArcher.getRunSpeed() * bonus; |
|
|
|
case 14103: |
|
if (this.isWalk()) |
|
if (this.isCombat()) |
|
return Enum.Guards.UndeadArcher.getWalkCombatSpeed() * bonus; |
|
else return Enum.Guards.UndeadArcher.getWalkSpeed() * bonus; |
|
else |
|
return Enum.Guards.UndeadArcher.getRunSpeed() * bonus; |
|
} |
|
} |
|
//return combat speeds |
|
if (this.isCombat()) { |
|
if (this.isWalk()) { |
|
if (this.mobBase.getWalkCombat() <= 0) |
|
return MBServerStatics.MOB_SPEED_WALKCOMBAT * bonus; |
|
return this.mobBase.getWalkCombat() * bonus; |
|
} else { |
|
if (this.mobBase.getRunCombat() <= 0) |
|
return MBServerStatics.MOB_SPEED_RUNCOMBAT * bonus; |
|
return this.mobBase.getRunCombat() * bonus; |
|
} |
|
//not combat return normal speeds |
|
} else { |
|
if (this.isWalk()) { |
|
if (this.mobBase.getWalk() <= 0) |
|
return MBServerStatics.MOB_SPEED_WALK * bonus; |
|
return this.mobBase.getWalk() * bonus; |
|
} else { |
|
if (this.mobBase.getRun() <= 0) |
|
return MBServerStatics.MOB_SPEED_RUN * bonus; |
|
return this.mobBase.getRun() * bonus; |
|
} |
|
} |
|
|
|
} |
|
|
|
@Override |
|
public float getPassiveChance(String type, int AttackerLevel, boolean fromCombat) { |
|
//TODO add this later for dodge |
|
return 0f; |
|
} |
|
|
|
/** |
|
* @ Kill this Character |
|
*/ |
|
@Override |
|
public void killCharacter(AbstractCharacter attacker) { |
|
|
|
|
|
this.stopMovement(this.getMovementLoc()); |
|
|
|
if (attacker != null) { |
|
|
|
if (attacker.getObjectType() == GameObjectType.PlayerCharacter) { |
|
Group g = GroupManager.getGroup((PlayerCharacter) attacker); |
|
|
|
// Give XP, now handled inside the Experience Object |
|
if (!this.isPet() && !this.isNecroPet() && !this.isSummonedPet() && !this.isPlayerGuard) |
|
Experience.doExperience((PlayerCharacter) attacker, this, g); |
|
} else if (attacker.getObjectType().equals(GameObjectType.Mob)) { |
|
Mob mobAttacker = (Mob) attacker; |
|
|
|
if (mobAttacker.isPet()) { |
|
|
|
PlayerCharacter owner = mobAttacker.getOwner(); |
|
|
|
if (owner != null) { |
|
|
|
if (!this.isPet() && !this.isNecroPet() && !this.isSummonedPet() && !this.isPlayerGuard) { |
|
Group g = GroupManager.getGroup(owner); |
|
|
|
// Give XP, now handled inside the Experience Object |
|
Experience.doExperience(owner, this, g); |
|
} |
|
|
|
} |
|
} |
|
} |
|
} |
|
killCleanup(); |
|
} |
|
|
|
public void updateLocation() { |
|
|
|
if (!this.isMoving()) |
|
return; |
|
|
|
if (state == STATE.Disabled) |
|
return; |
|
|
|
if (this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { |
|
//Target is stunned or rooted. Don't move |
|
|
|
this.stopMovement(this.getMovementLoc()); |
|
|
|
return; |
|
} |
|
|
|
Vector3fImmutable newLoc = this.getMovementLoc(); |
|
|
|
if (newLoc.equals(this.getEndLoc())) { |
|
this.stopMovement(newLoc); |
|
return; |
|
//Next upda |
|
} |
|
|
|
setLoc(newLoc); |
|
//Next update will be end Loc, lets stop him here. |
|
|
|
} |
|
|
|
/* |
|
* Database |
|
*/ |
|
|
|
@Override |
|
public void killCharacter(String reason) { |
|
killCleanup(); |
|
} |
|
|
|
private void killCleanup() { |
|
Dispatch dispatch; |
|
|
|
try { |
|
if (this.isSiege) { |
|
this.deathTime = System.currentTimeMillis(); |
|
this.state = STATE.Dead; |
|
try { |
|
this.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
this.combatTarget = null; |
|
this.hasLoot = false; |
|
this.playerAgroMap.clear(); |
|
|
|
this.timeToSpawnSiege = System.currentTimeMillis() + 60 * 15 * 1000; |
|
|
|
if (this.isPet()) { |
|
|
|
PlayerCharacter petOwner = this.getOwner(); |
|
|
|
if (petOwner != null) { |
|
this.setOwner(null); |
|
petOwner.setPet(null); |
|
PetMsg petMsg = new PetMsg(5, null); |
|
dispatch = Dispatch.borrow(this.getOwner(), petMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); |
|
} |
|
} |
|
|
|
} else if (this.isPet() || this.isNecroPet()) { |
|
this.state = STATE.Disabled; |
|
|
|
this.combatTarget = null; |
|
this.hasLoot = false; |
|
|
|
if (this.parentZone != null) |
|
this.parentZone.zoneMobSet.remove(this); |
|
|
|
try { |
|
this.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
this.playerAgroMap.clear(); |
|
WorldGrid.RemoveWorldObject(this); |
|
|
|
DbManager.removeFromCache(this); |
|
|
|
// YEAH BONUS CODE! THANKS UNNAMED ASSHOLE! |
|
//WorldServer.removeObject(this); |
|
//WorldGrid.INSTANCE.removeWorldObject(this); |
|
//owner.getPet().disableIntelligence(); |
|
|
|
PlayerCharacter petOwner = this.getOwner(); |
|
|
|
if (petOwner != null) { |
|
this.setOwner(null); |
|
petOwner.setPet(null); |
|
PetMsg petMsg = new PetMsg(5, null); |
|
dispatch = Dispatch.borrow(petOwner, petMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); |
|
} |
|
} else { |
|
|
|
//cleanup effects |
|
|
|
this.deathTime = System.currentTimeMillis(); |
|
this.state = STATE.Dead; |
|
|
|
playerAgroMap.clear(); |
|
|
|
if (!this.isPlayerGuard) { |
|
|
|
ArrayList<MobLoot> alml = LootTable.getMobLootDeath(this, this.getLevel(), this.getLootTable()); |
|
|
|
for (MobLoot ml : alml) { |
|
this.charItemManager.addItemToInventory(ml); |
|
} |
|
|
|
if (this.equip != null) { |
|
|
|
for (MobEquipment me : equip.values()) { |
|
if (me.getDropChance() == 0) |
|
continue; |
|
|
|
float chance = ThreadLocalRandom.current().nextFloat(); |
|
|
|
if (chance <= me.getDropChance()) { |
|
MobLoot ml = new MobLoot(this, me.getItemBase(), false); |
|
ml.setFidelityEquipID(me.getObjectUUID()); |
|
this.charItemManager.addItemToInventory(ml); |
|
} |
|
} |
|
} |
|
} |
|
|
|
} |
|
try { |
|
this.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
this.combat = false; |
|
this.walkMode = true; |
|
this.combatTarget = null; |
|
|
|
this.hasLoot = this.charItemManager.getInventoryCount() > 0; |
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} |
|
} |
|
|
|
public void respawn() { |
|
//Commenting out Mob ID rotation. |
|
|
|
this.despawned = false; |
|
this.playerAgroMap.clear(); |
|
this.setCombatTarget(null); |
|
this.setHealth(this.healthMax); |
|
this.stamina.set(this.staminaMax); |
|
this.mana.set(this.manaMax); |
|
this.combat = false; |
|
this.walkMode = true; |
|
this.combatTarget = null; |
|
this.isAlive.set(true); |
|
|
|
if (!this.isSiege) |
|
this.lastBindLoc = Mob.GetSpawnRadiusLocation(this); |
|
else |
|
this.lastBindLoc = this.bindLoc; |
|
this.bindLoc = this.lastBindLoc; |
|
this.setLoc(this.lastBindLoc); |
|
this.stopMovement(this.lastBindLoc); |
|
NPCManager.applyRuneSetEffects(this); |
|
this.recalculateStats(); |
|
|
|
this.setHealth(this.healthMax); |
|
|
|
if (!this.isSiege && !this.isPlayerGuard && contract == null) |
|
loadInventory(); |
|
|
|
} |
|
|
|
public void despawn() { |
|
|
|
this.despawned = true; |
|
|
|
//WorldServer.removeObject(this); |
|
WorldGrid.RemoveWorldObject(this); |
|
this.charItemManager.clearInventory(); |
|
this.despawnTime = System.currentTimeMillis(); |
|
// this.setLoc(Vector3fImmutable.ZERO); |
|
} |
|
|
|
//Sets the relative position to a parent zone |
|
public void setRelPos(Zone zone, float locX, float locY, float locZ) { |
|
|
|
//update mob zone map |
|
|
|
if (this.parentZone != null) |
|
this.parentZone.zoneMobSet.remove(this); |
|
|
|
zone.zoneMobSet.add(this); |
|
|
|
this.statLat = locX; |
|
this.statAlt = locY; |
|
this.statLon = locZ; |
|
this.parentZone = zone; |
|
this.setBindLoc(new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ)); |
|
} |
|
|
|
public boolean canRespawn() { |
|
return System.currentTimeMillis() > this.despawnTime + 4000; |
|
} |
|
|
|
@Override |
|
public boolean canBeLooted() { |
|
return !this.isAlive(); |
|
} |
|
|
|
public int getTypeMasks() { |
|
|
|
if (this.mobBase == null) |
|
return 0; |
|
|
|
return this.mobBase.getTypeMasks(); |
|
} |
|
|
|
/** |
|
* Clears and sets the inventory of the Mob. Must be called every time the |
|
* mob is spawned or respawned. |
|
*/ |
|
private void loadInventory() { |
|
|
|
if (!MBServerStatics.ENABLE_MOB_LOOT) |
|
return; |
|
|
|
this.charItemManager.clearInventory(); |
|
this.charItemManager.clearEquip(); |
|
|
|
if (isPlayerGuard) |
|
return; |
|
|
|
int gold = Mob.randomGoldAmount(this); |
|
|
|
if (gold > 0 && this.getLootTable() != 0) { |
|
addGoldToInventory(gold); |
|
} |
|
|
|
//add random loot to mob |
|
ArrayList<MobLoot> alml = LootTable.getMobLoot(this, this.getLevel(), this.getLootTable(), false); //add hotzone check in later |
|
|
|
for (MobLoot ml : alml) { |
|
this.charItemManager.addItemToInventory(ml); |
|
} |
|
|
|
//send announcement if disc or godly rune |
|
for (Item it : this.getInventory()) { |
|
ItemBase ib = it.getItemBase(); |
|
if (ib.isDiscRune()) { |
|
//if disc rune send system message |
|
ChatSystemMsg chatMsg = new ChatSystemMsg(null, this.getName() + " in " + this.getParentZone().getName() + " has found the " + ib.getName() + ". Are you tough enough to take it?"); |
|
chatMsg.setMessageType(10); |
|
chatMsg.setChannel(Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
DispatchMessage.dispatchMsgToAll(chatMsg); |
|
} |
|
if (ib.isStatRune() && ib.getName().toLowerCase().contains("of the gods")) { |
|
//godly rune send system message |
|
ChatSystemMsg chatMsg = new ChatSystemMsg(null, this.getName() + " in " + this.getParentZone().getName() + " has found the " + ib.getName() + ". Are you tough enough to take it?"); |
|
chatMsg.setMessageType(10); |
|
chatMsg.setChannel(Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
DispatchMessage.dispatchMsgToAll(chatMsg); |
|
return; |
|
} |
|
} |
|
|
|
//add special loot to mob |
|
} |
|
|
|
private int getLootTable() { |
|
|
|
if (this.mobBase == null) |
|
return 0; |
|
|
|
return this.mobBase.getLootTable(); |
|
} |
|
|
|
/** |
|
* Sets the quantity of gold in the inventory. Calling this multiple times |
|
* will overwrite the gold amount. |
|
* |
|
* @param quantity Quantity of gold. |
|
*/ |
|
private void addGoldToInventory(int quantity) { |
|
MobLoot gold = new MobLoot(this, quantity); |
|
this.charItemManager.addItemToInventory(gold); |
|
} |
|
|
|
@Override |
|
public void updateDatabase() { |
|
// DbManager.MobQueries.updateDatabase(this); |
|
} |
|
|
|
public int removeFromDatabase() { |
|
return DbManager.MobQueries.DELETE_MOB(this); |
|
} |
|
|
|
public void refresh() { |
|
if (this.isAlive()) |
|
WorldGrid.updateObject(this); |
|
} |
|
|
|
public void recalculateStats() { |
|
|
|
try { |
|
calculateModifiedStats(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
try { |
|
calculateAtrDefenseDamage(); |
|
} catch (Exception e) { |
|
Logger.error(this.getMobBaseID() + " /" + e.getMessage()); |
|
} |
|
try { |
|
calculateMaxHealthManaStamina(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
Resists.calculateResists(this); |
|
} |
|
|
|
public void calculateMaxHealthManaStamina() { |
|
float h = 1f; |
|
float m = 0f; |
|
float s = 0f; |
|
|
|
h = this.mobBase.getHealthMax(); |
|
m = this.statSpiCurrent; |
|
s = this.statConCurrent; |
|
|
|
// Apply any bonuses from runes and effects |
|
if (this.bonuses != null) { |
|
h += this.bonuses.getFloat(ModType.HealthFull, SourceType.None); |
|
m += this.bonuses.getFloat(ModType.ManaFull, SourceType.None); |
|
s += this.bonuses.getFloat(ModType.StaminaFull, SourceType.None); |
|
|
|
//apply effects percent modifiers. DO THIS LAST! |
|
h *= (1 + this.bonuses.getFloatPercentAll(ModType.HealthFull, SourceType.None)); |
|
m *= (1 + this.bonuses.getFloatPercentAll(ModType.ManaFull, SourceType.None)); |
|
s *= (1 + this.bonuses.getFloatPercentAll(ModType.StaminaFull, SourceType.None)); |
|
} |
|
|
|
// Set max health, mana and stamina |
|
if (h > 0) |
|
this.healthMax = h; |
|
else |
|
this.healthMax = 1; |
|
|
|
if (m > -1) |
|
this.manaMax = m; |
|
else |
|
this.manaMax = 0; |
|
|
|
if (s > -1) |
|
this.staminaMax = s; |
|
else |
|
this.staminaMax = 0; |
|
|
|
// Update health, mana and stamina if needed |
|
if (this.getHealth() > this.healthMax) |
|
this.setHealth(this.healthMax); |
|
|
|
if (this.mana.get() > this.manaMax) |
|
this.mana.set(this.manaMax); |
|
|
|
if (this.stamina.get() > this.staminaMax) |
|
this.stamina.set(staminaMax); |
|
|
|
} |
|
|
|
public void calculateAtrDefenseDamage() { |
|
|
|
if (this.charItemManager == null || this.equip == null) { |
|
Logger.error("Player " + currentID + " missing skills or equipment"); |
|
defaultAtrAndDamage(true); |
|
defaultAtrAndDamage(false); |
|
this.defenseRating = 0; |
|
return; |
|
} |
|
|
|
try { |
|
calculateAtrDamageForWeapon( |
|
this.equip.get(MBServerStatics.SLOT_MAINHAND), true, this.equip.get(MBServerStatics.SLOT_OFFHAND)); |
|
} catch (Exception e) { |
|
|
|
this.atrHandOne = (short) this.mobBase.getAttackRating(); |
|
this.minDamageHandOne = (short) this.mobBase.getMinDmg(); |
|
this.maxDamageHandOne = (short) this.mobBase.getMaxDmg(); |
|
this.rangeHandOne = 6.5f; |
|
this.speedHandOne = 20; |
|
Logger.info("Mobbase ID " + this.getMobBaseID() + " returned an error. setting to default ATR and Damage." + e.getMessage()); |
|
} |
|
|
|
try { |
|
calculateAtrDamageForWeapon(this.equip.get(MBServerStatics.SLOT_OFFHAND), false, this.equip.get(MBServerStatics.SLOT_MAINHAND)); |
|
|
|
} catch (Exception e) { |
|
|
|
this.atrHandTwo = (short) this.mobBase.getAttackRating(); |
|
this.minDamageHandTwo = (short) this.mobBase.getMinDmg(); |
|
this.maxDamageHandTwo = (short) this.mobBase.getMaxDmg(); |
|
this.rangeHandTwo = 6.5f; |
|
this.speedHandTwo = 20; |
|
Logger.info("Mobbase ID " + this.getMobBaseID() + " returned an error. setting to default ATR and Damage." + e.getMessage()); |
|
} |
|
|
|
try { |
|
float defense = this.mobBase.getDefenseRating(); |
|
defense += getShieldDefense(equip.get(MBServerStatics.SLOT_OFFHAND)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_HELMET)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_CHEST)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_ARMS)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_GLOVES)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_LEGGINGS)); |
|
defense += getArmorDefense(equip.get(MBServerStatics.SLOT_FEET)); |
|
defense += getWeaponDefense(equip); |
|
|
|
if (this.bonuses != null) { |
|
// add any bonuses |
|
defense += (short) this.bonuses.getFloat(ModType.DCV, SourceType.None); |
|
|
|
// Finally multiply any percent modifiers. DO THIS LAST! |
|
float pos_Bonus = 1 + this.bonuses.getFloatPercentPositive(ModType.DCV, SourceType.None); |
|
|
|
|
|
defense = (short) (defense * pos_Bonus); |
|
|
|
//Lucky rune applies next |
|
|
|
float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.DCV, SourceType.None); |
|
defense = (short) (defense * (1 + neg_Bonus)); |
|
|
|
|
|
} else { |
|
// TODO add error log here |
|
Logger.error("Error: missing bonuses"); |
|
} |
|
|
|
defense = (defense < 1) ? 1 : defense; |
|
this.defenseRating = (short) (defense + 0.5f); |
|
} catch (Exception e) { |
|
Logger.info("Mobbase ID " + this.getMobBaseID() + " returned an error. Setting to Default Defense." + e.getMessage()); |
|
this.defenseRating = (short) this.mobBase.getDefense(); |
|
} |
|
// calculate defense for equipment |
|
} |
|
|
|
private float getWeaponDefense(HashMap<Integer, MobEquipment> equipped) { |
|
MobEquipment weapon = equipped.get(MBServerStatics.SLOT_MAINHAND); |
|
ItemBase wb = null; |
|
CharacterSkill skill, mastery; |
|
float val = 0; |
|
boolean unarmed = false; |
|
if (weapon == null) { |
|
weapon = equipped.get(MBServerStatics.SLOT_OFFHAND); |
|
|
|
if (weapon == null) |
|
unarmed = true; |
|
else |
|
wb = weapon.getItemBase(); |
|
|
|
} else |
|
wb = weapon.getItemBase(); |
|
|
|
if (wb == null) |
|
unarmed = true; |
|
|
|
if (unarmed) { |
|
skill = null; |
|
mastery = null; |
|
} else { |
|
skill = this.skills.get(wb.getSkillRequired()); |
|
mastery = this.skills.get(wb.getMastery()); |
|
} |
|
|
|
if (skill != null) |
|
val += (int) skill.getModifiedAmount() / 2f; |
|
|
|
if (mastery != null) |
|
val += (int) mastery.getModifiedAmount() / 2f; |
|
|
|
return val; |
|
} |
|
|
|
private float getShieldDefense(MobEquipment shield) { |
|
|
|
if (shield == null) |
|
return 0; |
|
|
|
ItemBase ab = shield.getItemBase(); |
|
|
|
if (ab == null || !ab.isShield()) |
|
return 0; |
|
|
|
CharacterSkill blockSkill = this.skills.get("Block"); |
|
float skillMod; |
|
|
|
if (blockSkill == null) { |
|
skillMod = CharacterSkill.getQuickMastery(this, "Block"); |
|
|
|
if (skillMod == 0f) |
|
return 0; |
|
|
|
} else |
|
skillMod = blockSkill.getModifiedAmount(); |
|
|
|
// // Only fighters and healers can block |
|
// if (this.baseClass != null && (this.baseClass.getUUID() == 2500 || this.baseClass.getUUID() == 2501)) |
|
// this.bonuses.setBool("Block", true); |
|
|
|
float def = ab.getDefense(); |
|
//apply item defense bonuses |
|
// float val = ((float)ab.getDefense()) * (1 + (skillMod / 100)); |
|
return (def * (1 + ((int) skillMod / 100f))); |
|
} |
|
|
|
private float getArmorDefense(MobEquipment armor) { |
|
|
|
if (armor == null) |
|
return 0; |
|
|
|
ItemBase ib = armor.getItemBase(); |
|
|
|
if (ib == null) |
|
return 0; |
|
|
|
if (!ib.getType().equals(ItemType.ARMOR)) |
|
return 0; |
|
|
|
if (ib.getSkillRequired().isEmpty()) |
|
return ib.getDefense(); |
|
|
|
CharacterSkill armorSkill = this.skills.get(ib.getSkillRequired()); |
|
|
|
if (armorSkill == null) |
|
return ib.getDefense(); |
|
|
|
float def = ib.getDefense(); |
|
|
|
//apply item defense bonuses |
|
|
|
return (def * (1 + ((int) armorSkill.getModifiedAmount() / 50f))); |
|
} |
|
|
|
private void calculateAtrDamageForWeapon(MobEquipment weapon, boolean mainHand, MobEquipment otherHand) { |
|
|
|
int baseStrength = 0; |
|
|
|
float skillPercentage, masteryPercentage; |
|
float mastDam; |
|
|
|
// make sure weapon exists |
|
boolean noWeapon = false; |
|
ItemBase wb = null; |
|
|
|
if (weapon == null) |
|
noWeapon = true; |
|
else { |
|
|
|
ItemBase ib = weapon.getItemBase(); |
|
|
|
if (ib == null) |
|
noWeapon = true; |
|
else { |
|
|
|
if (ib.getType().equals(ItemType.WEAPON) == false) { |
|
defaultAtrAndDamage(mainHand); |
|
return; |
|
} else |
|
wb = ib; |
|
} |
|
} |
|
float min, max; |
|
float speed = 20f; |
|
boolean strBased = false; |
|
|
|
// get skill percentages and min and max damage for weapons |
|
|
|
if (noWeapon) { |
|
|
|
if (mainHand) |
|
this.rangeHandOne = this.mobBase.getAttackRange(); |
|
else |
|
this.rangeHandTwo = -1; // set to do not attack |
|
|
|
skillPercentage = getModifiedAmount(this.skills.get("Unarmed Combat")); |
|
masteryPercentage = getModifiedAmount(this.skills.get("Unarmed Combat Mastery")); |
|
|
|
if (masteryPercentage == 0f) |
|
mastDam = CharacterSkill.getQuickMastery(this, "Unarmed Combat Mastery"); |
|
else |
|
mastDam = masteryPercentage; |
|
|
|
// TODO Correct these |
|
min = this.mobBase.getMinDmg(); |
|
max = this.mobBase.getMaxDmg(); |
|
} else { |
|
|
|
if (mainHand) |
|
this.rangeHandOne = weapon.getItemBase().getRange() * (1 + (baseStrength / 600)); |
|
else |
|
this.rangeHandTwo = weapon.getItemBase().getRange() * (1 + (baseStrength / 600)); |
|
|
|
skillPercentage = getModifiedAmount(this.skills.get(wb.getSkillRequired())); |
|
masteryPercentage = getModifiedAmount(this.skills.get(wb.getMastery())); |
|
|
|
if (masteryPercentage == 0f) |
|
mastDam = 0f; |
|
else |
|
mastDam = masteryPercentage; |
|
|
|
min = wb.getMinDamage(); |
|
max = wb.getMaxDamage(); |
|
strBased = wb.isStrBased(); |
|
} |
|
|
|
// calculate atr |
|
float atr = this.mobBase.getAttackRating(); |
|
|
|
//atr += ((int) skillPercentage * 4f); //<-round down skill% - |
|
//atr += ((int) masteryPercentage * 3f); |
|
|
|
if (this.statStrCurrent > this.statDexCurrent) |
|
atr += statStrCurrent / 2; |
|
else |
|
atr += statDexCurrent / 2; |
|
|
|
// add in any bonuses to atr |
|
if (this.bonuses != null) { |
|
// Add any base bonuses |
|
atr += this.bonuses.getFloat(ModType.OCV, SourceType.None); |
|
|
|
// Finally use any multipliers. DO THIS LAST! |
|
float pos_Bonus = 1 + this.bonuses.getFloatPercentPositive(ModType.OCV, SourceType.None); |
|
|
|
|
|
atr *= pos_Bonus; |
|
|
|
// next precise |
|
|
|
// atr *= (1 + ((float) this.bonuses.getShort("rune.Attack") / 100)); |
|
|
|
//and negative percent modifiers |
|
//TODO DO DEBUFFS AFTER?? wILL TEst when finished |
|
float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.OCV, SourceType.None); |
|
|
|
|
|
atr *= (1 + neg_Bonus); |
|
} |
|
|
|
atr = (atr < 1) ? 1 : atr; |
|
|
|
// set atr |
|
if (mainHand) |
|
this.atrHandOne = (short) (atr + 0.5f); |
|
else |
|
this.atrHandTwo = (short) (atr + 0.5f); |
|
|
|
//calculate speed |
|
|
|
if (wb != null) |
|
speed = wb.getSpeed(); |
|
else |
|
speed = 20f; //unarmed attack speed |
|
|
|
if (this.bonuses != null && this.bonuses.getFloat(ModType.AttackDelay, SourceType.None) != 0f) //add effects speed bonus |
|
speed *= (1 + this.bonuses.getFloatPercentAll(ModType.AttackDelay, SourceType.None)); |
|
|
|
if (speed < 10) |
|
speed = 10; |
|
|
|
//add min/max damage bonuses for weapon **REMOVED |
|
|
|
//if duel wielding, cut damage by 30% |
|
// calculate damage |
|
float minDamage; |
|
float maxDamage; |
|
float pri = (strBased) ? (float) this.statStrCurrent : (float) this.statDexCurrent; |
|
float sec = (strBased) ? (float) this.statDexCurrent : (float) this.statStrCurrent; |
|
|
|
minDamage = (float) (min * ((0.0315f * Math.pow(pri, 0.75f)) + (0.042f * Math.pow(sec, 0.75f)) + (0.01f * ((int) skillPercentage + (int) mastDam)))); |
|
maxDamage = (float) (max * ((0.0785f * Math.pow(pri, 0.75f)) + (0.016f * Math.pow(sec, 0.75f)) + (0.0075f * ((int) skillPercentage + (int) mastDam)))); |
|
|
|
minDamage = (float) ((int) (minDamage + 0.5f)); //round to nearest decimal |
|
maxDamage = (float) ((int) (maxDamage + 0.5f)); //round to nearest decimal |
|
// Logger.info("MobCalculateDamage", "Mob with ID "+ this.getObjectUUID() + " and MOBBASE with ID " + this.getMobBaseID() + " returned " + minDamage + "/" + maxDamage + " modified Damage."); |
|
|
|
//add Base damage last. |
|
float minDamageMod = this.mobBase.getDamageMin(); |
|
float maxDamageMod = this.mobBase.getDamageMax(); |
|
|
|
minDamage += minDamageMod; |
|
maxDamage += maxDamageMod; |
|
|
|
// add in any bonuses to damage |
|
if (this.bonuses != null) { |
|
// Add any base bonuses |
|
minDamage += this.bonuses.getFloat(ModType.MinDamage, SourceType.None); |
|
maxDamage += this.bonuses.getFloat(ModType.MaxDamage, SourceType.None); |
|
|
|
// Finally use any multipliers. DO THIS LAST! |
|
minDamage *= (1 + this.bonuses.getFloatPercentAll(ModType.MinDamage, SourceType.None)); |
|
maxDamage *= (1 + this.bonuses.getFloatPercentAll(ModType.MaxDamage, SourceType.None)); |
|
} |
|
|
|
// set damages |
|
if (mainHand) { |
|
this.minDamageHandOne = (short) minDamage; |
|
this.maxDamageHandOne = (short) maxDamage; |
|
this.speedHandOne = 30; |
|
} else { |
|
this.minDamageHandTwo = (short) minDamage; |
|
this.maxDamageHandTwo = (short) maxDamage; |
|
this.speedHandTwo = 30; |
|
} |
|
} |
|
|
|
private void defaultAtrAndDamage(boolean mainHand) { |
|
|
|
if (mainHand) { |
|
this.atrHandOne = 0; |
|
this.minDamageHandOne = 0; |
|
this.maxDamageHandOne = 0; |
|
this.rangeHandOne = -1; |
|
this.speedHandOne = 20; |
|
} else { |
|
this.atrHandTwo = 0; |
|
this.minDamageHandTwo = 0; |
|
this.maxDamageHandTwo = 0; |
|
this.rangeHandTwo = -1; |
|
this.speedHandTwo = 20; |
|
} |
|
} |
|
|
|
public void setInBuildingLoc(Building inBuilding, AbstractCharacter ac) { |
|
|
|
Mob mob = null; |
|
|
|
NPC npc = null; |
|
|
|
|
|
if (ac.getObjectType().equals(GameObjectType.Mob)) |
|
mob = (Mob) ac; |
|
|
|
else if (ac.getObjectType().equals(GameObjectType.NPC)) |
|
npc = (NPC) ac; |
|
|
|
// *** Refactor : Need to take a look at this, make sure |
|
// npc's are loaded in correct spots. |
|
|
|
BuildingModelBase buildingModel = BuildingModelBase.getModelBase(inBuilding.getMeshUUID()); |
|
|
|
Vector3fImmutable slotLocation = Vector3fImmutable.ZERO; |
|
|
|
if (buildingModel != null) { |
|
|
|
|
|
int putSlot = -1; |
|
BuildingLocation buildingLocation = null; |
|
|
|
//-1 slot means no slot available in building. |
|
|
|
if (npc != null) { |
|
if (npc.getSiegeMinionMap().containsKey(this)) |
|
putSlot = npc.getSiegeMinionMap().get(this); |
|
} else if (mob != null) |
|
if (mob.getSiegeMinionMap().containsKey(this)) |
|
putSlot = mob.getSiegeMinionMap().get(this); |
|
|
|
int count = 0; |
|
|
|
for (BuildingLocation slotLoc : buildingModel.getLocations()) |
|
if (slotLoc.getType() == 6) |
|
count++; |
|
|
|
|
|
buildingLocation = buildingModel.getSlotLocation((count) - putSlot); |
|
|
|
if (buildingLocation != null) { |
|
slotLocation = buildingLocation.getLoc(); |
|
} |
|
|
|
} |
|
|
|
this.inBuildingLoc = slotLocation; |
|
|
|
} |
|
|
|
public Vector3fImmutable getInBuildingLoc() { |
|
return inBuildingLoc; |
|
} |
|
|
|
public ItemBase getWeaponItemBase(boolean mainHand) { |
|
|
|
if (this.equipmentSetID != 0) { |
|
|
|
if (equip != null) { |
|
MobEquipment me = null; |
|
|
|
if (mainHand) |
|
me = equip.get(1); //mainHand |
|
else |
|
me = equip.get(2); //offHand |
|
|
|
if (me != null) { |
|
|
|
ItemBase ib = me.getItemBase(); |
|
|
|
if (ib != null) |
|
return ib; |
|
|
|
} |
|
} |
|
} |
|
MobBase mb = this.mobBase; |
|
|
|
if (mb != null) { |
|
|
|
if (equip != null) { |
|
|
|
MobEquipment me = null; |
|
|
|
if (mainHand) |
|
me = equip.get(1); //mainHand |
|
else |
|
me = equip.get(2); //offHand |
|
|
|
if (me != null) { |
|
|
|
ItemBase ib = me.getItemBase(); |
|
|
|
return ib; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
@Override |
|
public void runAfterLoad() { |
|
|
|
try { |
|
if (this.equipmentSetID != 0) |
|
this.equip = MobBase.loadEquipmentSet(this.equipmentSetID); |
|
else |
|
this.equip = new HashMap<>(); |
|
|
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
mobPowers = new HashMap<Integer, Integer>(); |
|
//mobPowers = DbManager.MobBaseQueries.LOAD_STATIC_POWERS(this.getMobBaseID()); |
|
if (PowersManager.AllMobPowers.containsKey(this.getMobBaseID()) == true) { |
|
mobPowers = PowersManager.AllMobPowers.get(this.getMobBaseID()); |
|
} |
|
if (this.equip == null) { |
|
Logger.error("Null equipset returned for uuid " + currentID); |
|
this.equip = new HashMap<>(0); |
|
} |
|
|
|
try { |
|
NPCManager.applyRuneSetEffects(this); |
|
recalculateStats(); |
|
this.setHealth(this.healthMax); |
|
|
|
// Set bounds for this mobile |
|
Bounds mobBounds = Bounds.borrow(); |
|
mobBounds.setBounds(this.getLoc()); |
|
this.setBounds(mobBounds); |
|
|
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
} |
|
|
|
@Override |
|
protected ConcurrentHashMap<Integer, CharacterPower> initializePowers() { |
|
return new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); |
|
} |
|
|
|
public boolean canSee(PlayerCharacter target) { |
|
return this.mobBase.getSeeInvis() >= target.getHidden(); |
|
} |
|
|
|
public int getBuildingID() { |
|
return buildingID; |
|
} |
|
|
|
public void setBuildingID(int buildingID) { |
|
this.buildingID = buildingID; |
|
} |
|
|
|
public boolean isSiege() { |
|
return isSiege; |
|
} |
|
|
|
public void setSiege(boolean isSiege) { |
|
this.isSiege = isSiege; |
|
} |
|
|
|
public long getTimeToSpawnSiege() { |
|
return timeToSpawnSiege; |
|
} |
|
|
|
public void setTimeToSpawnSiege(long timeToSpawnSiege) { |
|
this.timeToSpawnSiege = timeToSpawnSiege; |
|
} |
|
|
|
public AbstractCharacter getNpcOwner() { |
|
return npcOwner; |
|
} |
|
|
|
public void setNpcOwner(AbstractCharacter npcOwner) { |
|
this.npcOwner = npcOwner; |
|
} |
|
|
|
public boolean isNecroPet() { |
|
return this.mobBase.isNecroPet(); |
|
} |
|
|
|
public void handleDirectAggro(AbstractCharacter ac) { |
|
|
|
if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) == false) |
|
return; |
|
|
|
PlayerCharacter player = (PlayerCharacter) ac; |
|
|
|
if (this.getCombatTarget() == null) { |
|
MobileFSM.setAggro(this, player.getObjectUUID()); |
|
return; |
|
} |
|
|
|
if (player.getObjectUUID() == this.getCombatTarget().getObjectUUID()) |
|
return; |
|
|
|
if (this.getCombatTarget().getObjectType() == GameObjectType.PlayerCharacter) { |
|
|
|
if (ac.getHateValue() > ((PlayerCharacter) this.getCombatTarget()).getHateValue()) { |
|
this.setCombatTarget(player); |
|
MobileFSM.setAggro(this, player.getObjectUUID()); |
|
} |
|
} |
|
} |
|
|
|
public boolean remove(Building building) { |
|
|
|
// Remove npc from it's building |
|
this.state = STATE.Disabled; |
|
|
|
try { |
|
this.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
if (this.parentZone != null) |
|
this.parentZone.zoneMobSet.remove(this); |
|
|
|
if (building != null) { |
|
building.getHirelings().remove(this); |
|
this.removeMinions(); |
|
} |
|
|
|
// Delete npc from database |
|
|
|
if (DbManager.MobQueries.DELETE_MOB(this) == 0) |
|
return false; |
|
|
|
// Remove npc from the simulation |
|
|
|
this.removeFromCache(); |
|
DbManager.removeFromCache(this); |
|
WorldGrid.RemoveWorldObject(this); |
|
WorldGrid.removeObject(this); |
|
return true; |
|
} |
|
|
|
public void removeMinions() { |
|
|
|
for (Mob toRemove : this.siegeMinionMap.keySet()) { |
|
|
|
toRemove.state = STATE.Disabled; |
|
|
|
if (this.isMoving()) { |
|
|
|
this.stopMovement(this.getLoc()); |
|
this.state = STATE.Disabled; |
|
|
|
if (toRemove.parentZone != null) |
|
toRemove.parentZone.zoneMobSet.remove(toRemove); |
|
} |
|
|
|
try { |
|
toRemove.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
if (toRemove.parentZone != null) |
|
toRemove.parentZone.zoneMobSet.remove(toRemove); |
|
|
|
WorldGrid.RemoveWorldObject(toRemove); |
|
WorldGrid.removeObject(toRemove); |
|
DbManager.removeFromCache(toRemove); |
|
|
|
PlayerCharacter petOwner = toRemove.getOwner(); |
|
|
|
if (petOwner != null) { |
|
|
|
petOwner.setPet(null); |
|
toRemove.setOwner(null); |
|
|
|
PetMsg petMsg = new PetMsg(5, null); |
|
Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); |
|
} |
|
} |
|
} |
|
|
|
public void setRank(int newRank) { |
|
|
|
DbManager.MobQueries.SET_PROPERTY(this, "mob_level", newRank); |
|
this.level = (short) newRank; |
|
|
|
} |
|
|
|
public boolean isRanking() { |
|
|
|
return this.upgradeDateTime != null; |
|
} |
|
|
|
public boolean isNoAggro() { |
|
return noAggro; |
|
} |
|
|
|
public void setNoAggro(boolean noAggro) { |
|
this.noAggro = noAggro; |
|
} |
|
|
|
public STATE getState() { |
|
return state; |
|
} |
|
|
|
public void setState(STATE state) { |
|
this.state = state; |
|
} |
|
|
|
public int getAggroTargetID() { |
|
return aggroTargetID; |
|
} |
|
|
|
public void setAggroTargetID(int aggroTargetID) { |
|
this.aggroTargetID = aggroTargetID; |
|
} |
|
|
|
public boolean isWalkingHome() { |
|
return walkingHome; |
|
} |
|
|
|
public void setWalkingHome(boolean walkingHome) { |
|
this.walkingHome = walkingHome; |
|
} |
|
|
|
public long getLastAttackTime() { |
|
return lastAttackTime; |
|
} |
|
|
|
public void setLastAttackTime(long lastAttackTime) { |
|
this.lastAttackTime = lastAttackTime; |
|
} |
|
|
|
public ConcurrentHashMap<Integer, Boolean> getPlayerAgroMap() { |
|
return playerAgroMap; |
|
} |
|
|
|
public long getDeathTime() { |
|
return deathTime; |
|
} |
|
|
|
public void setDeathTime(long deathTime) { |
|
this.deathTime = deathTime; |
|
} |
|
|
|
public boolean isHasLoot() { |
|
return hasLoot; |
|
} |
|
|
|
public DeferredPowerJob getWeaponPower() { |
|
return weaponPower; |
|
} |
|
|
|
public void setWeaponPower(DeferredPowerJob weaponPower) { |
|
this.weaponPower = weaponPower; |
|
} |
|
|
|
public ConcurrentHashMap<Mob, Integer> getSiegeMinionMap() { |
|
return siegeMinionMap; |
|
} |
|
|
|
public Building getBuilding() { |
|
return this.building; |
|
} |
|
|
|
public DateTime getUpgradeDateTime() { |
|
|
|
lock.readLock().lock(); |
|
|
|
try { |
|
return upgradeDateTime; |
|
} finally { |
|
lock.readLock().unlock(); |
|
} |
|
} |
|
|
|
public synchronized Mob createGuardMob(int loadID, Guild guild, Zone parent, Vector3fImmutable loc, short level, String pirateName) { |
|
|
|
MobBase minionMobBase; |
|
Mob mob; |
|
int maxSlots = 1; |
|
|
|
switch (this.getRank()) { |
|
case 1: |
|
case 2: |
|
maxSlots = 1; |
|
break; |
|
case 3: |
|
maxSlots = 2; |
|
break; |
|
case 4: |
|
case 5: |
|
maxSlots = 3; |
|
break; |
|
case 6: |
|
maxSlots = 4; |
|
break; |
|
case 7: |
|
maxSlots = 5; |
|
break; |
|
default: |
|
maxSlots = 1; |
|
|
|
} |
|
|
|
if (siegeMinionMap.size() == maxSlots) |
|
return null; |
|
|
|
minionMobBase = this.mobBase; |
|
|
|
if (minionMobBase == null) |
|
return null; |
|
|
|
mob = new Mob(minionMobBase, guild, parent, level, new Vector3fImmutable(1, 1, 1), 0, true); |
|
|
|
mob.despawned = true; |
|
|
|
mob.setLevel(level); |
|
//grab equipment and name from minionbase. |
|
if (this.contract != null) { |
|
MinionType minionType = MinionType.ContractToMinionMap.get(this.contract.getContractID()); |
|
if (minionType != null) { |
|
mob.equipmentSetID = minionType.getEquipSetID(); |
|
String rank = ""; |
|
|
|
if (this.getRank() < 3) |
|
rank = MBServerStatics.JUNIOR; |
|
else if (this.getRank() < 6) |
|
rank = ""; |
|
else if (this.getRank() == 6) |
|
rank = MBServerStatics.VETERAN; |
|
else |
|
rank = MBServerStatics.ELITE; |
|
|
|
if (rank.isEmpty()) |
|
mob.nameOverride = pirateName + " " + minionType.getRace() + " " + minionType.getName(); |
|
else |
|
mob.nameOverride = pirateName + " " + minionType.getRace() + " " + rank + " " + minionType.getName(); |
|
} |
|
} |
|
|
|
|
|
if (parent != null) |
|
mob.setRelPos(parent, loc.x - parent.absX, loc.y - parent.absY, loc.z - parent.absZ); |
|
|
|
mob.setObjectTypeMask(MBServerStatics.MASK_MOB | mob.getTypeMasks()); |
|
|
|
// mob.setMob(); |
|
mob.isPlayerGuard = true; |
|
mob.setParentZone(parent); |
|
DbManager.addToCache(mob); |
|
mob.runAfterLoad(); |
|
|
|
|
|
RuneBase guardRune = RuneBase.getRuneBase(252621); |
|
|
|
for (MobBaseEffects mbe : guardRune.getEffectsList()) { |
|
|
|
EffectsBase eb = PowersManager.getEffectByToken(mbe.getToken()); |
|
|
|
if (eb == null) { |
|
Logger.info("EffectsBase Null for Token " + mbe.getToken()); |
|
continue; |
|
} |
|
|
|
//check to upgrade effects if needed. |
|
if (mob.effects.containsKey(Integer.toString(eb.getUUID()))) { |
|
if (mbe.getReqLvl() > (int) mob.level) { |
|
continue; |
|
} |
|
|
|
Effect eff = mob.effects.get(Integer.toString(eb.getUUID())); |
|
|
|
if (eff == null) |
|
continue; |
|
|
|
//Current effect is a higher rank, dont apply. |
|
if (eff.getTrains() > mbe.getRank()) |
|
continue; |
|
|
|
//new effect is of a higher rank. remove old effect and apply new one. |
|
eff.cancelJob(); |
|
mob.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); |
|
} else { |
|
|
|
if (mbe.getReqLvl() > (int) mob.level) |
|
continue; |
|
|
|
mob.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); |
|
} |
|
} |
|
|
|
int slot = 0; |
|
slot += siegeMinionMap.size() + 1; |
|
|
|
siegeMinionMap.put(mob, slot); |
|
mob.setInBuildingLoc(this.building, this); |
|
mob.setBindLoc(loc.add(mob.inBuildingLoc)); |
|
mob.deathTime = System.currentTimeMillis(); |
|
mob.spawnTime = 900; |
|
mob.npcOwner = this; |
|
mob.state = STATE.Respawn; |
|
|
|
return mob; |
|
} |
|
|
|
public Contract getContract() { |
|
return contract; |
|
} |
|
|
|
public void setContract(Contract contract) { |
|
this.contract = contract; |
|
} |
|
|
|
public boolean isPlayerGuard() { |
|
return isPlayerGuard; |
|
} |
|
|
|
public void setPlayerGuard(boolean isPlayerGuard) { |
|
this.isPlayerGuard = isPlayerGuard; |
|
} |
|
|
|
public int getPatrolPointIndex() { |
|
return patrolPointIndex; |
|
} |
|
|
|
public void setPatrolPointIndex(int patrolPointIndex) { |
|
this.patrolPointIndex = patrolPointIndex; |
|
} |
|
|
|
public int getLastMobPowerToken() { |
|
return lastMobPowerToken; |
|
} |
|
|
|
public void setLastMobPowerToken(int lastMobPowerToken) { |
|
this.lastMobPowerToken = lastMobPowerToken; |
|
} |
|
|
|
public Regions getLastRegion() { |
|
return lastRegion; |
|
} |
|
|
|
public void setLastRegion(Regions lastRegion) { |
|
this.lastRegion = lastRegion; |
|
} |
|
|
|
public boolean isLootSync() { |
|
return lootSync; |
|
} |
|
|
|
public void setLootSync(boolean lootSync) { |
|
this.lootSync = lootSync; |
|
} |
|
|
|
public int getFidalityID() { |
|
return fidalityID; |
|
} |
|
|
|
public HashMap<Integer, MobEquipment> getEquip() { |
|
return equip; |
|
} |
|
|
|
public int getEquipmentSetID() { |
|
return equipmentSetID; |
|
} |
|
|
|
public int getLootSet() { |
|
return lootSet; |
|
} |
|
|
|
public boolean isGuard() { |
|
return this.isGuard; |
|
} |
|
|
|
public String getNameOverride() { |
|
return nameOverride; |
|
} |
|
|
|
public void processUpgradeMob(PlayerCharacter player) { |
|
|
|
lock.writeLock().lock(); |
|
|
|
try { |
|
|
|
building = this.getBuilding(); |
|
|
|
// Cannot upgrade an npc not within a building |
|
|
|
if (building == null) |
|
return; |
|
|
|
// Cannot upgrade an npc at max rank |
|
|
|
if (this.getRank() == 7) |
|
return; |
|
|
|
// Cannot upgrade an npc who is currently ranking |
|
|
|
if (this.isRanking()) |
|
return; |
|
|
|
int rankCost = Mob.getUpgradeCost(this); |
|
|
|
// SEND NOT ENOUGH GOLD ERROR |
|
|
|
if (rankCost > building.getStrongboxValue()) { |
|
sendErrorPopup(player, 127); |
|
return; |
|
} |
|
|
|
try { |
|
|
|
if (!building.transferGold(-rankCost, false)) { |
|
return; |
|
} |
|
|
|
DateTime dateToUpgrade = DateTime.now().plusHours(Mob.getUpgradeTime(this)); |
|
Mob.setUpgradeDateTime(this, dateToUpgrade); |
|
|
|
// Schedule upgrade job |
|
|
|
Mob.submitUpgradeJob(this); |
|
|
|
} catch (Exception e) { |
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
} |
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} finally { |
|
lock.writeLock().unlock(); |
|
} |
|
} |
|
|
|
public void processRedeedMob(ClientConnection origin) { |
|
|
|
// Member variable declaration |
|
PlayerCharacter player; |
|
Contract contract; |
|
CharacterItemManager itemMan; |
|
ItemBase itemBase; |
|
Item item; |
|
|
|
this.lock.writeLock().lock(); |
|
|
|
try { |
|
|
|
player = SessionManager.getPlayerCharacter(origin); |
|
itemMan = player.getCharItemManager(); |
|
|
|
|
|
contract = this.getContract(); |
|
|
|
if (!player.getCharItemManager().hasRoomInventory((short) 1)) { |
|
ErrorPopupMsg.sendErrorPopup(player, 21); |
|
return; |
|
} |
|
|
|
|
|
if (!building.getHirelings().containsKey(this)) |
|
return; |
|
|
|
if (!this.remove(building)) { |
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
return; |
|
} |
|
|
|
building.getHirelings().remove(this); |
|
|
|
itemBase = ItemBase.getItemBase(contract.getContractID()); |
|
|
|
if (itemBase == null) { |
|
Logger.error("Could not find Contract for npc: " + this.getObjectUUID()); |
|
return; |
|
} |
|
|
|
boolean itemWorked = false; |
|
|
|
item = new Item(itemBase, player.getObjectUUID(), OwnerType.PlayerCharacter, (byte) ((byte) this.getRank() - 1), (byte) ((byte) this.getRank() - 1), |
|
(short) 1, (short) 1, true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, |
|
new ArrayList<>(), ""); |
|
item.setNumOfItems(1); |
|
item.containerType = Enum.ItemContainerType.INVENTORY; |
|
|
|
try { |
|
item = DbManager.ItemQueries.ADD_ITEM(item); |
|
itemWorked = true; |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} |
|
if (itemWorked) { |
|
itemMan.addItemToInventory(item); |
|
itemMan.updateInventory(); |
|
} |
|
|
|
ManageCityAssetsMsg mca = new ManageCityAssetsMsg(); |
|
mca.actionType = NPC.SVR_CLOSE_WINDOW; |
|
mca.setTargetType(building.getObjectType().ordinal()); |
|
mca.setTargetID(building.getObjectUUID()); |
|
origin.sendMsg(mca); |
|
|
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} finally { |
|
this.lock.writeLock().unlock(); |
|
} |
|
|
|
} |
|
|
|
public void dismiss() { |
|
|
|
if (this.isPet()) { |
|
|
|
if (this.isSummonedPet()) { //delete summoned pet |
|
|
|
WorldGrid.RemoveWorldObject(this); |
|
DbManager.removeFromCache(this); |
|
if (this.getObjectType() == GameObjectType.Mob) { |
|
this.setState(STATE.Disabled); |
|
if (this.getParentZone() != null) |
|
this.getParentZone().zoneMobSet.remove(this); |
|
} |
|
|
|
} else { //revert charmed pet |
|
this.setMob(); |
|
this.setCombatTarget(null); |
|
// if (this.isAlive()) |
|
// WorldServer.updateObject(this); |
|
} |
|
//clear owner |
|
PlayerCharacter owner = this.getOwner(); |
|
|
|
//close pet window |
|
if (owner != null) { |
|
Mob pet = owner.getPet(); |
|
PetMsg pm = new PetMsg(5, null); |
|
Dispatch dispatch = Dispatch.borrow(owner, pm); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
if (pet != null && pet.getObjectUUID() == this.getObjectUUID()) |
|
owner.setPet(null); |
|
|
|
if (this.getObjectType().equals(GameObjectType.Mob)) |
|
this.setOwner(null); |
|
} |
|
|
|
|
|
} |
|
} |
|
|
|
public void dismissNecroPet(boolean updateOwner) { |
|
|
|
this.state = STATE.Disabled; |
|
|
|
this.combatTarget = null; |
|
this.hasLoot = false; |
|
|
|
if (this.parentZone != null) |
|
this.parentZone.zoneMobSet.remove(this); |
|
|
|
try { |
|
this.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
this.playerAgroMap.clear(); |
|
WorldGrid.RemoveWorldObject(this); |
|
|
|
DbManager.removeFromCache(this); |
|
|
|
// YEAH BONUS CODE! THANKS UNNAMED ASSHOLE! |
|
//WorldServer.removeObject(this); |
|
//WorldGrid.INSTANCE.removeWorldObject(this); |
|
//owner.getPet().disableIntelligence(); |
|
|
|
PlayerCharacter petOwner = this.getOwner(); |
|
|
|
if (petOwner != null) { |
|
this.setOwner(null); |
|
petOwner.setPet(null); |
|
|
|
if (updateOwner == false) |
|
return; |
|
PetMsg petMsg = new PetMsg(5, null); |
|
Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); |
|
} |
|
} |
|
|
|
|
|
}
|
|
|