// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.objects; import ch.claude_martin.enumbitset.EnumBitSet; import engine.Enum; import engine.Enum.*; import engine.InterestManagement.WorldGrid; import engine.ai.MobileFSM; import engine.ai.utilities.MovementUtilities; 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.loot.LootManager; 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.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 mobMapByDBID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); //mob specific public final ConcurrentHashMap 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 mobPowers; protected int dbID; //the database ID protected int loadID; protected boolean isMob; public MobBase mobBase; protected float spawnRadius; public int spawnTime; //used by static mobs protected int parentZoneID; public Zone parentZone; protected float statLat; protected float statLon; protected float statAlt; public Building building; public Contract contract; private int currentID; private int ownerUID = 0; //only used by pets public boolean hasLoot = false; private AbstractWorldObject fearedObject = null; private int buildingID; private boolean isSiege = false; public boolean isPlayerGuard = false; private long timeToSpawnSiege; public AbstractCharacter npcOwner; public Vector3fImmutable inBuildingLoc = null; private boolean noAggro = false; private int aggroTargetID = 0; private boolean walkingHome = true; private long lastAttackTime = 0; public long deathTime = 0; public final ConcurrentHashMap siegeMinionMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); private int patrolPointIndex = 0; private int lastMobPowerToken = 0; private HashMap equip = null; public String nameOverride = ""; private Regions lastRegion = null; private long despawnTime = 0; private DeferredPowerJob weaponPower; private DateTime upgradeDateTime = null; private boolean lootSync = false; public int equipmentSetID = 0; public int runeSet = 0; public int bootySet = 0; public EnumBitSet notEnemy; public EnumBitSet enemy; public MobBehaviourType BehaviourType; public ArrayList patrolPoints; public int lastPatrolPointIndex = 0; public long stopPatrolTime = 0; /** * 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; 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(); this.BehaviourType = Enum.MobBehaviourType.Pet1; 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() + " Captain"; } int guildID = rs.getInt("mob_guildUID"); 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.equipmentSetID = rs.getInt("equipmentSet"); this.runeSet = rs.getInt("runeSet"); this.bootySet = rs.getInt("bootySet"); this.notEnemy = EnumBitSet.asEnumBitSet(rs.getLong("notEnemy"), Enum.MonsterType.class); this.enemy = EnumBitSet.asEnumBitSet(rs.getLong("enemy"), Enum.MonsterType.class); if (this.contract != null) this.equipmentSetID = this.contract.getEquipmentSet(); this.nameOverride = rs.getString("mob_name"); if(rs.getString("fsm").length() > 1){ this.BehaviourType = Enum.MobBehaviourType.valueOf(rs.getString("fsm")); } } 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 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; } 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; //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); if (this.building != null && this.building.getBlueprint() != null && this.isPlayerGuard()) { int maxSlots = 10; for (int slot = 1; slot < maxSlots + 1; slot++) if (!this.building.getHirelings().containsValue(slot)) { this.building.getHirelings().put(this, slot); break; } } } /* * 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; } if ( this.building != null) { Vector3fImmutable localLoc = new Vector3fImmutable(this.statLat,this.statAlt,this.statLon); Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(this.building, localLoc); this.setBindLoc(buildingWorldLoc); this.setLoc(buildingWorldLoc); this.endLoc = buildingWorldLoc; this.stopMovement(endLoc); return; } Vector3fImmutable localLoc = new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ); this.setBindLoc(localLoc); this.setLoc(localLoc); this.endLoc = localLoc; this.stopMovement(endLoc); } 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); this.region = AbstractWorldObject.GetRegionByWorldObject(this); return; //Next upda } setLoc(newLoc); this.region = AbstractWorldObject.GetRegionByWorldObject(this); //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; MobileFSM.dead(this); 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; MobileFSM.dead(this); playerAgroMap.clear(); if (!this.isPlayerGuard && this.equip != null) { LootManager.GenerateMobLoot(this, true); } } 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); this.deathTime = 0; 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.deathTime; } @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. */ public void loadInventory() { if (!MBServerStatics.ENABLE_MOB_LOOT) return; this.charItemManager.clearInventory(); this.charItemManager.clearEquip(); if (isPlayerGuard) return; LootManager.GenerateMobLoot(this, false); } 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 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 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(); //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); } // Combine mobbase and mob aggro arrays into one bitvector try { this.notEnemy.addAll(this.getMobBase().notEnemy); this.enemy.addAll(this.getMobBase().enemy); } catch(Exception ex){ } 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); //assign 5 random patrol points for regular mobs if(!this.isGuard() && !this.isPlayerGuard() && !this.isPet() && !this.isNecroPet() && !this.isSummonedPet() && !this.isCharmedPet()){ this.patrolPoints = new ArrayList<>(); for(int i = 0; i < 5; ++i){ float patrolRadius = this.getSpawnRadius(); if (patrolRadius > 256) patrolRadius = 256; if (patrolRadius < 60) patrolRadius = 60; Vector3fImmutable newPatrolPoint = Vector3fImmutable.getRandomPointInCircle(this.getBindLoc(), patrolRadius); this.patrolPoints.add(newPatrolPoint); } } if(this.BehaviourType == null) { this.BehaviourType = this.getMobBase().fsm; } if(this.isPlayerGuard() && this.contract != null){ this.BehaviourType = Enum.MobBehaviourType.GuardCaptain; } } catch (Exception e) { Logger.error(e.getMessage()); } } @Override protected ConcurrentHashMap 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 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()); this.combatTarget = ac; 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 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 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 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 getSiegeMinionMap() { return siegeMinionMap; } public DateTime getUpgradeDateTime() { lock.readLock().lock(); try { return upgradeDateTime; } finally { lock.readLock().unlock(); } } 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 HashMap getEquip() { return equip; } public int getEquipmentSetID() { return equipmentSetID; } public String getNameOverride() { return nameOverride; } public void processUpgradeMob(PlayerCharacter player) { lock.writeLock().lock(); try { building = this.building; // 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 (!NPCManager.removeMobileFromBuilding(this, 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.state = 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); } } } }