// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// 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.exception.SerializationException ;
import engine.gameManager.* ;
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.msg.PetMsg ;
import engine.net.client.msg.PlaceAssetMsg ;
import engine.server.MBServerStatics ;
import org.jetbrains.annotations.NotNull ;
import org.joda.time.DateTime ;
import org.pmw.tinylog.Logger ;
import java.sql.ResultSet ;
import java.sql.SQLException ;
import java.util.EnumSet ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.Delayed ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.locks.ReentrantReadWriteLock ;
import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup ;
import static java.lang.Math.toIntExact ;
public class Mob extends AbstractIntelligenceAgent implements Delayed {
private static int staticID = 0 ;
//mob specific
public final ConcurrentHashMap < Integer , Float > playerAgroMap = new ConcurrentHashMap < > ( ) ; //key = Player value = hate value
public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock ( ) ;
public long nextCastTime = 0 ;
public long nextCallForHelp = 0 ;
public ReentrantReadWriteLock minionLock = new ReentrantReadWriteLock ( ) ;
public boolean despawned = false ;
public Vector3fImmutable destination = Vector3fImmutable . ZERO ;
public MobBase mobBase ;
public int spawnDelay ;
public Zone parentZone ;
public boolean hasLoot = false ;
public long deathTime = 0 ;
public long respawnTime = 0 ;
public int equipmentSetID = 0 ;
public int runeSet = 0 ;
public int bootySet = 0 ;
public int loadID ;
public float spawnRadius ;
//used by static mobs
public int parentZoneUUID ;
//TODO implement feared object system
public AbstractWorldObject fearedObject = null ;
protected int dbID ; //the database ID
private int currentID ;
private long lastAttackTime = 0 ;
private int lastMobPowerToken = 0 ;
private DeferredPowerJob weaponPower ;
private DateTime upgradeDateTime = null ;
private boolean lootSync = false ;
// New Mobile constructor. Fill in the blanks and then call
// PERSIST.
public Mob ( ) {
super ( ) ;
this . dbID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
this . currentID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
this . bindLoc = Vector3fImmutable . ZERO ;
this . gridObjectType = GridObjectType . DYNAMIC ;
this . agentType = AIAgentType . MOBILE ;
}
/ * *
* ResultSet Constructor
* /
public Mob ( ResultSet rs ) throws SQLException {
super ( rs ) ;
float statLat ;
float statAlt ;
float statLon ;
try {
this . dbID = rs . getInt ( 1 ) ;
this . loadID = rs . getInt ( "mob_mobbaseID" ) ;
this . gridObjectType = GridObjectType . DYNAMIC ;
this . agentType = AIAgentType . MOBILE ;
this . spawnRadius = rs . getFloat ( "mob_spawnRadius" ) ;
this . spawnDelay = rs . getInt ( "mob_spawnTime" ) ;
statLat = rs . getFloat ( "mob_spawnX" ) ;
statAlt = rs . getFloat ( "mob_spawnY" ) ;
statLon = rs . getFloat ( "mob_spawnZ" ) ;
this . bindLoc = new Vector3fImmutable ( statLat , statAlt , statLon ) ;
this . parentZoneUUID = rs . getInt ( "parent" ) ;
this . level = ( short ) rs . getInt ( "mob_level" ) ;
this . buildingUUID = rs . getInt ( "mob_buildingID" ) ;
this . contractUUID = rs . getInt ( "mob_contractID" ) ;
this . guildUUID = rs . getInt ( "mob_guildUID" ) ;
this . equipmentSetID = rs . getInt ( "equipmentSet" ) ;
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 ) ;
if ( this . mobBase ! = null & & this . spawnDelay = = 0 )
this . spawnDelay = this . mobBase . getSpawnTime ( ) ;
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 ) ;
this . firstName = rs . getString ( "mob_name" ) ;
if ( rs . getString ( "fsm" ) . length ( ) > 1 )
this . behaviourType = MobBehaviourType . valueOf ( rs . getString ( "fsm" ) ) ;
this . currentID = this . dbID ;
} catch ( Exception e ) {
Logger . error ( e + " " + this . dbID ) ;
}
}
public static void serializeMobForClientMsgOtherPlayer ( Mob mob , ByteBufferWriter writer ) 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 ;
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 ) ;
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 ) ;
}
writer . putVector3f ( mob . getLoc ( ) ) ;
//Rotation
float radians = ( float ) Math . acos ( mob . getRot ( ) . y ) * 2 ;
if ( mob . building ! = null )
if ( mob . building . getBounds ( ) ! = null & & mob . building . getBounds ( ) . getQuaternion ( ) ! = null )
radians + = ( mob . building . getBounds ( ) . getQuaternion ( ) ) . angleY ;
writer . putFloat ( radians ) ;
//Inventory Stuff
writer . putInt ( 0 ) ;
// get a copy of the equipped items.
if ( ! mob . charItemManager . equipped . isEmpty ( ) ) {
writer . putInt ( mob . charItemManager . equipped . size ( ) ) ;
for ( Item me : mob . charItemManager . equipped . values ( ) )
Item . _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 ) ;
if ( mob . building ! = null & & mob . region ! = null ) {
writer . putInt ( mob . building . getObjectType ( ) . ordinal ( ) ) ;
writer . putInt ( mob . building . getObjectUUID ( ) ) ;
} else {
writer . putInt ( 0 ) ; //<-Building Object Type
writer . putInt ( 0 ) ; //<-Building Object ID
}
writer . put ( ( byte ) 0 ) ;
writer . put ( ( byte ) 0 ) ;
writer . put ( ( byte ) 0 ) ;
writer . putInt ( 0 ) ; // NPC menu options
if ( mob . contract ! = null & & mob . guardCaptain = = 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 . guardCaptain ! = null ) {
writer . put ( ( byte ) 1 ) ;
writer . putInt ( GameObjectType . PlayerCharacter . ordinal ( ) ) ;
writer . putInt ( 131117009 ) ;
writer . putInt ( mob . guardCaptain . getObjectType ( ) . ordinal ( ) ) ;
writer . putInt ( mob . guardCaptain . getObjectUUID ( ) ) ;
writer . putInt ( 8 ) ;
} else
writer . put ( ( byte ) 0 ) ;
if ( mob . isPet ( ) ) {
writer . put ( ( byte ) 1 ) ;
if ( mob . guardCaptain ! = null ) {
writer . putInt ( mob . guardCaptain . getObjectType ( ) . ordinal ( ) ) ;
writer . putInt ( mob . guardCaptain . 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 . behaviourType . equals ( MobBehaviourType . SiegeEngine ) & & ! mob . isPlayerGuard ( ) ) {
writer . putInt ( 0 ) ;
writer . putInt ( 0 ) ;
}
writer . put ( ( byte ) 0 ) ;
Guild . _serializeForClientMsg ( mob . getGuild ( ) , writer ) ;
if ( mob . mobBase ! = null & & mob . mobBase . getObjectUUID ( ) = = 100570 ) {
writer . putInt ( 2 ) ;
writer . putInt ( 0x00008A2E ) ;
writer . putInt ( 0x1AB84003 ) ;
} else if ( mob . behaviourType . equals ( MobBehaviourType . SiegeEngine ) ) {
writer . putInt ( 1 ) ;
writer . putInt ( 74620179 ) ;
} else
writer . putInt ( 0 ) ;
writer . putInt ( 0 ) ; //0xB8400300
writer . putInt ( 0 ) ;
//TODO Guard
writer . put ( ( byte ) 0 ) ;
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 ;
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 ) ;
}
public static Mob createMob ( int loadID , Vector3fImmutable spawn , Guild guild , Zone parent , Building building , Contract contract , String pirateName , int level , AIAgentType mobType ) {
Mob mobile = new Mob ( ) ;
mobile . dbID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
mobile . agentType = mobType ;
mobile . behaviourType = MobBehaviourType . None ;
mobile . loadID = loadID ;
mobile . level = ( short ) level ;
if ( guild = = null | | guild . isEmptyGuild ( ) )
mobile . guildUUID = 0 ;
else
mobile . guildUUID = guild . getObjectUUID ( ) ;
mobile . parentZoneUUID = parent . getObjectUUID ( ) ;
if ( building = = null )
mobile . buildingUUID = 0 ;
else
mobile . buildingUUID = building . getObjectUUID ( ) ;
if ( mobile . buildingUUID ! = 0 )
mobile . bindLoc = Vector3fImmutable . ZERO ;
else
mobile . bindLoc = ZoneManager . worldToLocal ( spawn , parent ) ;
;
mobile . firstName = pirateName ;
if ( contract = = null )
mobile . contractUUID = 0 ;
else
mobile . contractUUID = contract . getContractID ( ) ;
return mobile ;
}
public static synchronized Mob createGuardMinion ( Mob guardCaptain , short level , String minionName ) {
Mob minionMobile ;
minionMobile = new Mob ( ) ;
minionMobile . currentID = ( - - Mob . staticID ) ;
minionMobile . level = level ;
minionMobile . loadID = guardCaptain . loadID ;
minionMobile . firstName = minionName ;
minionMobile . equipmentSetID = guardCaptain . equipmentSetID ;
minionMobile . buildingUUID = guardCaptain . building . getObjectUUID ( ) ;
minionMobile . guildUUID = guardCaptain . guildUUID ;
minionMobile . runeSet = guardCaptain . runeSet ;
minionMobile . enemy = guardCaptain . enemy ;
minionMobile . notEnemy = guardCaptain . notEnemy ;
minionMobile . deathTime = System . currentTimeMillis ( ) ;
minionMobile . guardCaptain = guardCaptain ;
minionMobile . spawnDelay = ( int ) ( - 2 . 500 * guardCaptain . building . getRank ( ) + 22 . 5 ) * 60 ;
minionMobile . behaviourType = Enum . MobBehaviourType . GuardMinion ;
minionMobile . agentType = AIAgentType . GUARDMINION ;
minionMobile . guardedCity = guardCaptain . guardedCity ;
minionMobile . patrolPoints = guardCaptain . building . patrolPoints ;
minionMobile . parentZoneUUID = guardCaptain . parentZoneUUID ;
minionMobile . bindLoc = Vector3fImmutable . ZERO ;
//grab name from minionbase.
Enum . MinionType minionType = Enum . MinionType . ContractToMinionMap . get ( guardCaptain . contract . getContractID ( ) ) ;
if ( minionType ! = null ) {
String rank ;
if ( guardCaptain . getRank ( ) < 3 )
rank = MBServerStatics . JUNIOR ;
else if ( guardCaptain . getRank ( ) < 6 )
rank = "" ;
else if ( guardCaptain . getRank ( ) = = 6 )
rank = MBServerStatics . VETERAN ;
else
rank = MBServerStatics . ELITE ;
minionMobile . lastName = rank + " " + minionType . getRace ( ) + " " + minionType . getName ( ) ;
}
// Configure and spawn minion
minionMobile . runAfterLoad ( ) ;
DbManager . addToCache ( minionMobile ) ;
minionMobile . setLoc ( minionMobile . bindLoc ) ;
minionMobile . despawn ( ) ;
guardCaptain . minions . add ( minionMobile . getObjectUUID ( ) ) ;
return minionMobile ;
}
public static synchronized Mob createSiegeMinion ( NPC artyCaptain , int loadID ) {
Mob siegeMinion ;
siegeMinion = new Mob ( ) ;
siegeMinion . currentID = ( - - Mob . staticID ) ;
siegeMinion . level = 1 ;
siegeMinion . loadID = loadID ;
siegeMinion . guildUUID = artyCaptain . guildUUID ;
siegeMinion . equipmentSetID = 0 ;
siegeMinion . buildingUUID = artyCaptain . buildingUUID ;
siegeMinion . guardCaptain = artyCaptain ;
siegeMinion . parentZoneUUID = artyCaptain . parentZoneUUID ;
siegeMinion . behaviourType = MobBehaviourType . SiegeEngine ;
siegeMinion . agentType = AIAgentType . SIEGEENGINE ;
siegeMinion . bindLoc = Vector3fImmutable . ZERO ;
siegeMinion . spawnDelay = ( 60 * 15 ) ;
siegeMinion . runAfterLoad ( ) ;
DbManager . addToCache ( siegeMinion ) ;
siegeMinion . setLoc ( siegeMinion . bindLoc ) ;
siegeMinion . despawn ( ) ;
artyCaptain . minions . add ( siegeMinion . getObjectUUID ( ) ) ;
return siegeMinion ;
}
public static synchronized Mob createPetMinion ( int loadID , Zone parent , PlayerCharacter petOwner , short level ) {
if ( petOwner = = null )
return null ;
Mob petMinion = new Mob ( ) ;
petMinion . currentID = ( - - Mob . staticID ) ;
petMinion . level = ( short ) ( level + 20 ) ;
petMinion . loadID = loadID ;
petMinion . bindLoc = petOwner . getLoc ( ) ;
petMinion . loc = petOwner . getLoc ( ) ;
petMinion . guardCaptain = petOwner ;
petMinion . parentZoneUUID = parent . getObjectUUID ( ) ;
petMinion . walkMode = false ;
petMinion . healthMax = MobBase . getMobBase ( loadID ) . getHealthMax ( ) * ( petMinion . level * 0 . 5f ) ;
petMinion . health . set ( petMinion . healthMax ) ;
petMinion . behaviourType = MobBehaviourType . Pet1 ;
petMinion . agentType = AIAgentType . PET ;
petMinion . firstName = "" ;
petMinion . lastName = "" ;
petMinion . despawned = false ;
petMinion . runAfterLoad ( ) ;
DbManager . addToCache ( petMinion ) ;
petMinion . setLoc ( petMinion . bindLoc ) ;
return petMinion ;
}
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 ) ;
}
private static float getModifiedAmount ( CharacterSkill skill ) {
if ( skill = = null )
return 0f ;
return skill . getModifiedAmount ( ) ;
}
public static void submitUpgradeJob ( Mob mob ) {
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 ( ) ) )
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 ;
}
/ *
* Getters
* /
@Override
public int getDBID ( ) {
return this . dbID ;
}
public int getLoadID ( ) {
return loadID ;
}
/ *
* Serialization
* /
@Override
public int getObjectUUID ( ) {
return currentID ;
}
public float getSpawnRadius ( ) {
return this . spawnRadius ;
}
public String getSpawnTimeAsString ( ) {
if ( this . spawnDelay = = 0 )
return MBServerStatics . DEFAULT_SPAWN_TIME_MS / 1000 + " seconds (Default)" ;
else
return this . spawnDelay + " seconds" ;
}
@Override
public MobBase getMobBase ( ) {
return this . mobBase ;
}
public int getMobBaseID ( ) {
return this . mobBase . getObjectUUID ( ) ;
}
public Vector3fImmutable getTrueBindLoc ( ) {
return this . bindLoc ;
}
public Zone getParentZone ( ) {
return this . parentZone ;
}
@Override
public int getGuildUUID ( ) {
if ( this . guild = = null )
return 0 ;
return this . guild . getObjectUUID ( ) ;
}
@Override
public Vector3fImmutable getBindLoc ( ) {
if ( this . isPet ( ) & & ! this . behaviourType . equals ( MobBehaviourType . SiegeEngine ) )
return this . guardCaptain ! = null ? this . guardCaptain . getLoc ( ) : this . getLoc ( ) ;
else
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 ) ) ;
}
// 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 Guards . HumanArcher . getWalkCombatSpeed ( ) * bonus ;
else
return Guards . HumanArcher . getWalkSpeed ( ) * bonus ;
else
return Guards . HumanArcher . getRunSpeed ( ) * bonus ;
case 14103 :
if ( this . isWalk ( ) )
if ( this . isCombat ( ) )
return Guards . UndeadArcher . getWalkCombatSpeed ( ) * bonus ;
else
return Guards . UndeadArcher . getWalkSpeed ( ) * bonus ;
else
return Guards . UndeadArcher . getRunSpeed ( ) * bonus ;
}
//return combat speeds
//not combat return normal 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 ;
}
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 ;
}
/ *
* Database
* /
/ * *
* @ 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 . agentType . equals ( AIAgentType . PET ) ) & & ! this . isPlayerGuard ( ) )
Experience . doExperience ( ( PlayerCharacter ) attacker , this , g ) ;
} else if ( attacker . getObjectType ( ) . equals ( GameObjectType . Mob ) ) {
Mob mobAttacker = ( Mob ) attacker ;
if ( mobAttacker . isPet ( ) ) {
PlayerCharacter owner = ( PlayerCharacter ) mobAttacker . guardCaptain ;
if ( owner ! = null )
if ( ! this . isPet ( ) & & ! this . isNecroPet ( ) & & ! ( this . agentType . equals ( AIAgentType . PET ) ) & & ! 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 ( 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.
}
@Override
public void killCharacter ( String reason ) {
killCleanup ( ) ;
}
private void killCleanup ( ) {
Dispatch dispatch ;
try {
//resync corpses
if ( this . behaviourType . equals ( MobBehaviourType . SiegeEngine ) ) {
this . deathTime = System . currentTimeMillis ( ) ;
try {
this . clearEffects ( ) ;
} catch ( Exception e ) {
Logger . error ( e . getMessage ( ) ) ;
}
this . setCombatTarget ( null ) ;
this . hasLoot = false ;
this . playerAgroMap . clear ( ) ;
if ( this . behaviourType . ordinal ( ) = = Enum . MobBehaviourType . GuardMinion . ordinal ( ) )
this . spawnDelay = ( int ) ( - 2 . 500 * this . guardCaptain . building . getRank ( ) + 22 . 5 ) * 60 ;
if ( this . isPet ( ) ) {
PlayerCharacter petOwner = ( PlayerCharacter ) this . guardCaptain ;
if ( petOwner ! = null ) {
this . guardCaptain = null ;
petOwner . setPet ( null ) ;
PetMsg petMsg = new PetMsg ( 5 , null ) ;
dispatch = Dispatch . borrow ( ( PlayerCharacter ) this . guardCaptain , petMsg ) ;
DispatchMessage . dispatchMsgDispatch ( dispatch , Enum . DispatchChannel . PRIMARY ) ;
}
}
} else if ( this . isPet ( ) | | this . isNecroPet ( ) ) {
this . setCombatTarget ( null ) ;
this . hasLoot = false ;
ZoneManager . seaFloor . zoneMobSet . remove ( this ) ;
try {
this . clearEffects ( ) ;
} catch ( Exception e ) {
Logger . error ( e . getMessage ( ) ) ;
}
this . playerAgroMap . clear ( ) ;
WorldGrid . RemoveWorldObject ( this ) ;
DbManager . removeFromCache ( this ) ;
PlayerCharacter petOwner = ( PlayerCharacter ) this . guardCaptain ;
if ( petOwner ! = null ) {
this . guardCaptain = null ;
petOwner . setPet ( null ) ;
PetMsg petMsg = new PetMsg ( 5 , null ) ;
dispatch = Dispatch . borrow ( petOwner , petMsg ) ;
DispatchMessage . dispatchMsgDispatch ( dispatch , Enum . DispatchChannel . PRIMARY ) ;
}
} else {
//cleanup effects
playerAgroMap . clear ( ) ;
if ( ! this . isPlayerGuard ( ) & & this . charItemManager . equipped ! = null )
LootManager . GenerateEquipmentDrop ( this ) ;
}
try {
this . clearEffects ( ) ;
} catch ( Exception e ) {
Logger . error ( e . getMessage ( ) ) ;
}
this . combat = false ;
this . walkMode = true ;
this . setCombatTarget ( null ) ;
this . hasLoot = this . charItemManager . getInventoryCount ( ) > 0 ;
} catch ( Exception e ) {
Logger . error ( e ) ;
}
this . updateLocation ( ) ;
}
public void respawn ( ) {
this . despawned = false ;
this . setCombatTarget ( null ) ;
this . setHealth ( this . healthMax ) ;
this . stamina . set ( this . staminaMax ) ;
this . mana . set ( this . manaMax ) ;
this . combat = false ;
this . walkMode = true ;
this . setCombatTarget ( null ) ;
this . isAlive . set ( true ) ;
this . deathTime = 0 ;
this . lastBindLoc = this . bindLoc ;
this . setLoc ( this . lastBindLoc ) ;
this . stopMovement ( this . lastBindLoc ) ;
NPCManager . applyMobbaseEffects ( this ) ;
this . recalculateStats ( ) ;
this . setHealth ( this . healthMax ) ;
if ( this . building = = null & & this . guardCaptain ! = null & & ( ( Mob ) this . guardCaptain ) . behaviourType . equals ( MobBehaviourType . GuardCaptain ) )
this . building = this . guardCaptain . building ;
this . loadInventory ( ) ;
this . updateLocation ( ) ;
}
public void despawn ( ) {
this . despawned = true ;
WorldGrid . RemoveWorldObject ( this ) ;
this . charItemManager . clearInventory ( ) ;
}
@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 ( ) ;
// Reload equipment set
this . charItemManager . equipped = MobBase . loadEquipmentSet ( this . equipmentSetID ) ;
// Only generate loot for mobiles
if ( ! this . agentType . equals ( AIAgentType . MOBILE ) )
return ;
LootManager . GenerateMobLoot ( this ) ;
}
@Override
public void updateDatabase ( ) {
}
public void refresh ( ) {
if ( this . isAlive ( ) )
WorldGrid . updateObject ( this ) ;
}
public void recalculateStats ( ) {
if ( this . isPlayerGuard ( ) ) {
NPCManager . setMaxHealthForGuard ( this ) ;
NPCManager . setAttackRatingForGuard ( this ) ;
NPCManager . setDefenseForGuard ( this ) ;
NPCManager . setDamageAndSpeedForGuard ( this ) ;
NPCManager . applyGuardStanceModifiers ( this ) ;
} else {
AbstractCharacter . calculateAtrDamageForWeapon ( this , this . charItemManager . equipped . get ( EquipSlotType . RHELD ) , true , this . charItemManager . equipped . get ( EquipSlotType . LHELD ) ) ;
AbstractCharacter . calculateAtrDefenseDamage ( this ) ;
calculateMaxHealthManaStamina ( ) ;
}
calculateModifiedStats ( ) ;
if ( this . isSiege ( ) )
this . healthMax = 10000 ;
Resists . calculateResists ( this ) ;
}
public void calculateMaxHealthManaStamina ( ) {
float h ;
float m ;
float s ;
h = this . mobBase . getHealthMax ( ) ;
if ( this . isPet ( ) ) {
h = this . level * 0 . 5f * 120 ;
}
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 ) ;
}
@Override
public void runAfterLoad ( ) {
this . setObjectTypeMask ( MBServerStatics . MASK_MOB | this . getTypeMasks ( ) ) ;
if ( ConfigManager . serverType . equals ( ServerType . LOGINSERVER ) )
return ;
this . mobBase = MobBase . getMobBase ( loadID ) ;
this . building = BuildingManager . getBuilding ( this . buildingUUID ) ;
// Configure AI related values
switch ( this . behaviourType ) {
case GuardCaptain :
this . agentType = AIAgentType . GUARDCAPTAIN ;
this . spawnDelay = 600 ;
if ( this . building = = null )
Logger . error ( "Captain : " + this . getObjectUUID ( ) + " missing building " + this . buildingUUID ) ;
this . guardedCity = ZoneManager . getCityAtLocation ( this . building . getLoc ( ) ) ;
break ;
case GuardWallArcher :
this . agentType = AIAgentType . GUARDWALLARCHER ;
this . spawnDelay = 450 ;
if ( this . building = = null )
Logger . error ( "Wall Archer : " + this . getObjectUUID ( ) + " missing building " + this . buildingUUID ) ;
this . guardedCity = ZoneManager . getCityAtLocation ( this . building . getLoc ( ) ) ;
break ;
}
// Default to the mobbase for AI if nothing is in mob field to override.
if ( this . behaviourType = = null | | this . behaviourType . equals ( MobBehaviourType . None ) )
this . behaviourType = this . getMobBase ( ) . fsm ;
if ( this . behaviourType = = null )
this . behaviourType = MobBehaviourType . None ;
if ( this . contractUUID = = 0 )
this . contract = null ;
else
this . contract = DbManager . ContractQueries . GET_CONTRACT ( this . contractUUID ) ;
// Setup equipset for contract
if ( this . contract ! = null )
this . equipmentSetID = this . contract . getEquipmentSet ( ) ;
// Mobiles default to the building guild.
if ( this . building ! = null )
this . guild = this . building . getGuild ( ) ;
else
this . guild = Guild . getGuild ( guildUUID ) ;
if ( this . guild = = null )
this . guild = Guild . getErrantGuild ( ) ;
if ( this . firstName . isEmpty ( ) )
this . firstName = this . mobBase . getFirstName ( ) ;
if ( this . contract ! = null )
if ( this . lastName . isEmpty ( ) )
this . lastName = this . getContract ( ) . getName ( ) ;
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 ) ;
// Don't override level for guard minions or pets
if ( this . contract = = null )
if ( ! this . agentType . equals ( AIAgentType . GUARDMINION ) & & ! this . agentType . equals ( AIAgentType . PET ) )
this . level = ( short ) this . mobBase . getLevel ( ) ;
//set bonuses
this . bonuses = new PlayerBonuses ( this ) ;
//TODO set these correctly later
this . rangeHandOne = this . mobBase . getAttackRange ( ) ;
this . rangeHandTwo = - 1 ;
this . minDamageHandOne = ( int ) this . mobBase . getDamageMin ( ) ;
this . maxDamageHandOne = ( int ) this . mobBase . getDamageMax ( ) ;
this . minDamageHandTwo = 0 ;
this . maxDamageHandTwo = 0 ;
this . atrHandOne = this . mobBase . getAttackRating ( ) ;
this . defenseRating = ( short ) this . mobBase . getDefenseRating ( ) ;
this . isActive = true ;
// Configure parent zone adding this NPC to the
// zone collection
this . parentZone = ZoneManager . getZoneByUUID ( this . parentZoneUUID ) ;
this . parentZone . zoneMobSet . remove ( this ) ;
this . parentZone . zoneMobSet . add ( this ) ;
// Handle Mobiles within buildings
if ( this . building = = null ) {
// Do not adjust a pet's bindloc.
if ( ! this . agentType . equals ( AIAgentType . PET ) )
this . bindLoc = this . parentZone . getLoc ( ) . add ( this . bindLoc ) ;
} else {
// Mobiles inside buildings are offset from it not the zone
// with the exceptions being mobiles
// with a contract.
if ( this . contract ! = null | | this . behaviourType . equals ( MobBehaviourType . SiegeEngine ) )
NPCManager . slotCharacterInBuilding ( this ) ;
else
this . bindLoc = building . getLoc ( ) . add ( bindLoc ) ;
}
// Setup location for this Mobile
this . setLoc ( bindLoc ) ;
this . endLoc = new Vector3fImmutable ( bindLoc ) ;
// Initialize inventory
this . charItemManager . load ( ) ;
this . loadInventory ( ) ;
if ( this . equipmentSetID ! = 0 )
this . charItemManager . equipped = MobBase . loadEquipmentSet ( this . equipmentSetID ) ;
// Combine mobbase and mob aggro arrays into one bitvector
//skip for pets
if ( this . isPet ( ) = = false & & this . isNecroPet ( ) = = false ) {
if ( this . getMobBase ( ) . notEnemy . size ( ) > 0 )
this . notEnemy . addAll ( this . getMobBase ( ) . notEnemy ) ;
if ( this . getMobBase ( ) . enemy . size ( ) > 0 )
this . enemy . addAll ( this . getMobBase ( ) . enemy ) ;
}
// Load skills, powers and effects
NPCManager . applyMobbaseEffects ( this ) ;
NPCManager . applyEquipmentResists ( this ) ;
NPCManager . applyMobbaseSkill ( this ) ;
NPCManager . applyRuneSkills ( this , this . getMobBaseID ( ) ) ;
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 . agentType . equals ( AIAgentType . MOBILE ) )
NPCManager . AssignPatrolPoints ( this ) ;
// Load minions for guard captain.
if ( this . agentType . equals ( AIAgentType . GUARDCAPTAIN ) )
DbManager . MobQueries . LOAD_GUARD_MINIONS ( this ) ;
this . deathTime = 0 ;
}
@Override
protected ConcurrentHashMap < Integer , CharacterPower > initializePowers ( ) {
return new ConcurrentHashMap < > ( MBServerStatics . CHM_INIT_CAP , MBServerStatics . CHM_LOAD , MBServerStatics . CHM_THREAD_LOW ) ;
}
public boolean canSee ( AbstractCharacter target ) {
return this . mobBase . getSeeInvis ( ) > = target . hidden ;
}
public int getBuildingID ( ) {
return buildingUUID ;
}
public void setBuildingID ( int buildingID ) {
this . buildingUUID = buildingID ;
}
public boolean isSiege ( ) {
return this . behaviourType . equals ( MobBehaviourType . SiegeEngine ) ;
}
public void setGuardCaptain ( AbstractCharacter guardCaptain ) {
this . guardCaptain = guardCaptain ;
}
public boolean isNecroPet ( ) {
return this . mobBase . isNecroPet ( ) ;
}
public void handleDirectAggro ( AbstractCharacter ac ) {
if ( ! ac . getObjectType ( ) . equals ( GameObjectType . PlayerCharacter ) )
return ;
if ( this . getCombatTarget ( ) = = null )
this . setCombatTarget ( ac ) ;
}
public void setRank ( int newRank ) {
DbManager . MobQueries . SET_PROPERTY ( this , "mob_level" , newRank ) ;
this . level = ( short ) newRank ;
this . recalculateStats ( ) ;
this . setHealth ( this . healthMax ) ;
}
public boolean isRanking ( ) {
return this . upgradeDateTime ! = null ;
}
public long getLastAttackTime ( ) {
return lastAttackTime ;
}
public void setDeathTime ( long deathTime ) {
this . deathTime = deathTime ;
}
public boolean isHasLoot ( ) {
return hasLoot ;
}
public void setWeaponPower ( DeferredPowerJob weaponPower ) {
this . weaponPower = weaponPower ;
}
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 EnumSet . of ( AIAgentType . GUARDCAPTAIN , AIAgentType . GUARDMINION , AIAgentType . GUARDWALLARCHER ) . contains ( this . agentType ) ;
}
public int getLastMobPowerToken ( ) {
return lastMobPowerToken ;
}
public void setLastMobPowerToken ( int lastMobPowerToken ) {
this . lastMobPowerToken = lastMobPowerToken ;
}
public boolean isLootSync ( ) {
return lootSync ;
}
public void setLootSync ( boolean lootSync ) {
this . lootSync = lootSync ;
}
public String getNameOverride ( ) {
return firstName + " " + lastName ;
}
public void processUpgradeMob ( PlayerCharacter player ) {
lock . writeLock ( ) . lock ( ) ;
try {
// 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 Boolean isGuard ( ) {
switch ( this . behaviourType ) {
case GuardMinion :
case GuardCaptain :
case GuardWallArcher :
case HamletGuard :
case SimpleStandingGuard :
return true ;
}
return false ;
}
@Override
public long getDelay ( @NotNull TimeUnit unit ) {
long timeRemaining = this . respawnTime - System . currentTimeMillis ( ) ;
return unit . convert ( timeRemaining , TimeUnit . MILLISECONDS ) ;
}
@Override
public int compareTo ( @NotNull Delayed o ) {
return toIntExact ( this . respawnTime - ( ( Mob ) o ) . respawnTime ) ;
}
}