2022-04-30 09:41:17 -04:00
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.objects ;
2023-04-22 07:52:05 -04:00
2022-04-30 09:41:17 -04:00
import engine.InterestManagement.WorldGrid ;
import engine.gameManager.* ;
import engine.job.JobScheduler ;
import engine.jobs.DeferredPowerJob ;
import engine.jobs.UpgradeNPCJob ;
import engine.math.Bounds ;
import engine.math.Vector3fImmutable ;
2024-04-05 07:59:44 -04:00
import engine.mbEnums ;
import engine.mbEnums.* ;
2022-04-30 09:41:17 -04:00
import engine.net.ByteBufferWriter ;
import engine.net.Dispatch ;
import engine.net.client.msg.PetMsg ;
import engine.net.client.msg.PlaceAssetMsg ;
import engine.server.MBServerStatics ;
2023-10-23 00:07:48 -04:00
import org.jetbrains.annotations.NotNull ;
2022-04-30 09:41:17 -04:00
import org.joda.time.DateTime ;
import org.pmw.tinylog.Logger ;
2023-04-22 07:52:05 -04:00
2022-04-30 09:41:17 -04:00
import java.sql.ResultSet ;
import java.sql.SQLException ;
2023-08-31 08:13:37 -04:00
import java.util.EnumSet ;
2022-04-30 09:41:17 -04:00
import java.util.concurrent.ConcurrentHashMap ;
2023-10-23 00:07:48 -04:00
import java.util.concurrent.Delayed ;
import java.util.concurrent.TimeUnit ;
2022-04-30 09:41:17 -04:00
import java.util.concurrent.locks.ReentrantReadWriteLock ;
2023-04-22 07:52:05 -04:00
2022-04-30 09:41:17 -04:00
import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup ;
2023-10-23 00:07:48 -04:00
import static java.lang.Math.toIntExact ;
2022-04-30 09:41:17 -04:00
2023-10-23 00:07:48 -04:00
public class Mob extends AbstractIntelligenceAgent implements Delayed {
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
private static int staticID = 0 ;
//mob specific
2023-08-29 21:01:00 -05:00
public final ConcurrentHashMap < Integer , Float > playerAgroMap = new ConcurrentHashMap <> (); //key = Player value = hate value
2023-08-27 21:26:00 -05:00
2023-04-23 11:19:34 -04:00
public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock ();
2023-03-15 08:47:22 -04:00
public long nextCastTime = 0 ;
public long nextCallForHelp = 0 ;
public ReentrantReadWriteLock minionLock = new ReentrantReadWriteLock ();
public boolean despawned = false ;
public Vector3fImmutable destination = Vector3fImmutable . ZERO ;
2023-03-29 08:05:34 -04:00
public MobBase mobBase ;
2023-10-23 00:07:48 -04:00
public int spawnDelay ;
2023-03-31 09:59:49 -04:00
public Zone parentZone ;
public boolean hasLoot = false ;
public long deathTime = 0 ;
2023-10-23 00:07:48 -04:00
public long respawnTime = 0 ;
2023-03-31 09:59:49 -04:00
public int equipmentSetID = 0 ;
2023-04-09 13:43:12 -04:00
public int runeSet = 0 ;
2023-04-07 12:22:33 -05:00
public int bootySet = 0 ;
2023-08-26 14:54:43 -04:00
2023-08-25 12:11:50 -04:00
public int loadID ;
public float spawnRadius ;
2023-04-22 07:52:05 -04:00
//used by static mobs
2023-08-25 12:11:50 -04:00
public int parentZoneUUID ;
2023-08-29 21:17:44 -05:00
//TODO implement feared object system
2023-08-27 21:26:00 -05:00
public AbstractWorldObject fearedObject = null ;
2023-09-01 14:17:07 -04:00
protected int dbID ; //the database ID
private int currentID ;
2024-03-28 22:15:48 -05:00
public long nextAttackTime = System . currentTimeMillis ();
2023-04-22 07:52:05 -04:00
private int lastMobPowerToken = 0 ;
private DeferredPowerJob weaponPower ;
private DateTime upgradeDateTime = null ;
private boolean lootSync = false ;
2023-08-21 16:12:06 -04:00
// New Mobile constructor. Fill in the blanks and then call
// PERSIST.
public Mob () {
2023-08-25 11:37:56 -04:00
super ();
2023-08-21 16:12:06 -04:00
this . dbID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
this . currentID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
2023-08-26 09:31:10 -04:00
this . bindLoc = Vector3fImmutable . ZERO ;
this . gridObjectType = GridObjectType . DYNAMIC ;
2023-08-29 21:17:44 -05:00
this . agentType = AIAgentType . MOBILE ;
2023-08-21 16:12:06 -04:00
}
2023-08-18 11:33:27 -04:00
2023-03-15 08:47:22 -04:00
/**
* ResultSet Constructor
*/
public Mob ( ResultSet rs ) throws SQLException {
super ( rs );
2023-08-26 06:24:05 -04:00
float statLat ;
float statAlt ;
float statLon ;
2023-03-30 10:32:08 -04:00
try {
2023-03-15 08:47:22 -04:00
this . dbID = rs . getInt ( 1 );
this . loadID = rs . getInt ( "mob_mobbaseID" );
this . gridObjectType = GridObjectType . DYNAMIC ;
2023-08-31 21:53:56 -04:00
this . agentType = AIAgentType . MOBILE ;
2023-08-26 09:31:10 -04:00
2023-03-15 08:47:22 -04:00
this . spawnRadius = rs . getFloat ( "mob_spawnRadius" );
2023-10-23 00:07:48 -04:00
this . spawnDelay = rs . getInt ( "mob_spawnTime" );
2023-03-15 08:47:22 -04:00
2023-08-26 06:24:05 -04:00
statLat = rs . getFloat ( "mob_spawnX" );
statAlt = rs . getFloat ( "mob_spawnY" );
statLon = rs . getFloat ( "mob_spawnZ" );
this . bindLoc = new Vector3fImmutable ( statLat , statAlt , statLon );
2023-03-15 08:47:22 -04:00
2023-08-25 11:59:59 -04:00
this . parentZoneUUID = rs . getInt ( "parent" );
2023-03-15 08:47:22 -04:00
this . level = ( short ) rs . getInt ( "mob_level" );
2023-08-23 12:24:43 -04:00
this . buildingUUID = rs . getInt ( "mob_buildingID" );
2023-03-15 08:47:22 -04:00
2023-08-23 12:24:43 -04:00
this . contractUUID = rs . getInt ( "mob_contractID" );
2023-03-15 08:47:22 -04:00
2023-08-23 12:24:43 -04:00
this . guildUUID = rs . getInt ( "mob_guildUID" );
2023-03-15 08:47:22 -04:00
2023-08-23 12:24:43 -04:00
this . equipmentSetID = rs . getInt ( "equipmentSet" );
2023-03-15 08:47:22 -04:00
java . util . Date sqlDateTime ;
sqlDateTime = rs . getTimestamp ( "upgradeDate" );
2023-04-30 10:55:16 -04:00
if ( sqlDateTime != null )
upgradeDateTime = new DateTime ( sqlDateTime );
2023-05-01 13:55:06 -04:00
else
upgradeDateTime = null ;
2023-03-15 08:47:22 -04:00
// Submit upgrade job if NPC is currently set to rank.
2023-05-01 13:55:06 -04:00
if ( this . upgradeDateTime != null )
Mob . submitUpgradeJob ( this );
2023-03-15 08:47:22 -04:00
2023-10-23 00:07:48 -04:00
if ( this . mobBase != null && this . spawnDelay == 0 )
this . spawnDelay = this . mobBase . getSpawnTime ();
2023-03-15 08:47:22 -04:00
2023-04-09 13:43:12 -04:00
this . runeSet = rs . getInt ( "runeSet" );
2023-04-07 12:22:33 -05:00
this . bootySet = rs . getInt ( "bootySet" );
2023-03-15 08:47:22 -04:00
2024-04-05 08:35:48 -04:00
this . notEnemy = mbEnums . fromLong ( rs . getLong ( "notEnemy" ), mbEnums . MonsterType . class );
this . enemy = mbEnums . fromLong ( rs . getLong ( "enemy" ), mbEnums . MonsterType . class );
2023-04-30 16:05:48 -05:00
this . firstName = rs . getString ( "mob_name" );
2023-05-02 09:10:41 -04:00
2023-05-01 13:55:06 -04:00
if ( rs . getString ( "fsm" ). length () > 1 )
2023-08-25 15:14:33 -04:00
this . behaviourType = MobBehaviourType . valueOf ( rs . getString ( "fsm" ));
2023-03-15 08:47:22 -04:00
2023-08-25 15:34:47 -04:00
this . currentID = this . dbID ;
2023-08-23 13:19:57 -04:00
2023-03-30 10:32:08 -04:00
} catch ( Exception e ) {
2023-04-29 13:57:09 -04:00
Logger . error ( e + " " + this . dbID );
2023-03-30 10:32:08 -04:00
}
2023-03-15 08:47:22 -04:00
}
2024-05-12 13:36:47 -04:00
public static void serializeMobForClientMsgOtherPlayer ( Mob mob , ByteBufferWriter writer ) {
2023-03-15 08:47:22 -04:00
Mob . serializeForClientMsgOtherPlayer ( mob , writer );
}
2024-05-12 13:36:47 -04:00
public static void serializeForClientMsgOtherPlayer ( Mob mob , ByteBufferWriter writer ) {
2023-03-15 08:47:22 -04:00
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
2023-08-31 08:13:37 -04:00
} else if ( mob . contract != null || mob . isPlayerGuard ()) {
2023-03-15 08:47:22 -04:00
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
2023-05-01 13:55:06 -04:00
} else
writer . putInt ( 1 );
2023-03-15 08:47:22 -04:00
//Generate Race Rune
writer . putInt ( 1 );
writer . putInt ( 0 );
2023-05-01 13:55:06 -04:00
if ( mob . mobBase != null )
writer . putInt ( mob . mobBase . getLoadID ());
else
writer . putInt ( mob . loadID );
2023-03-15 08:47:22 -04:00
writer . putInt ( mob . getObjectType (). ordinal ());
writer . putInt ( mob . currentID );
//Send Stats
2024-04-02 13:09:24 -04:00
2023-03-15 08:47:22 -04:00
writer . putInt ( 5 );
writer . putInt ( 0x8AC3C0E6 ); //Str
2024-04-02 13:09:24 -04:00
writer . putInt ( mob . statStrCurrent );
2023-03-15 08:47:22 -04:00
writer . putInt ( 0xACB82E33 ); //Dex
2024-04-02 13:09:24 -04:00
writer . putInt ( mob . statDexCurrent );
2023-03-15 08:47:22 -04:00
writer . putInt ( 0xB15DC77E ); //Con
2024-04-02 13:09:24 -04:00
writer . putInt ( mob . statConCurrent );
2023-03-15 08:47:22 -04:00
writer . putInt ( 0xE07B3336 ); //Int
2024-04-02 13:09:24 -04:00
writer . putInt ( mob . statIntCurrent );
2023-03-15 08:47:22 -04:00
writer . putInt ( 0xFF665EC3 ); //Spi
2024-04-02 13:09:24 -04:00
writer . putInt ( mob . statSpiCurrent );
2023-03-15 08:47:22 -04:00
2023-04-30 16:05:48 -05:00
2023-05-01 13:55:06 -04:00
writer . putString ( mob . firstName );
writer . putString ( mob . lastName );
2023-04-30 16:05:48 -05:00
2023-03-15 08:47:22 -04:00
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 );
}
2023-07-15 09:23:48 -04:00
writer . putVector3f ( mob . getLoc ());
2023-03-15 08:47:22 -04:00
//Rotation
2023-05-01 15:58:04 -04:00
float radians = ( float ) Math . acos ( mob . getRot (). y ) * 2 ;
2023-04-22 07:52:05 -04:00
2023-04-30 09:32:34 -04:00
if ( mob . building != null )
if ( mob . building . getBounds () != null && mob . building . getBounds (). getQuaternion () != null )
radians += ( mob . building . getBounds (). getQuaternion ()). angleY ;
writer . putFloat ( radians );
2023-03-15 08:47:22 -04:00
//Inventory Stuff
2023-04-22 07:52:05 -04:00
2023-03-15 08:47:22 -04:00
writer . putInt ( 0 );
// get a copy of the equipped items.
2024-03-18 09:38:33 -04:00
if ( ! mob . charItemManager . equipped . isEmpty ()) {
2023-03-15 08:47:22 -04:00
2024-03-18 09:38:33 -04:00
writer . putInt ( mob . charItemManager . equipped . size ());
2023-03-15 08:47:22 -04:00
2024-03-18 09:38:33 -04:00
for ( Item me : mob . charItemManager . equipped . values ())
Item . _serializeForClientMsg ( me , writer );
2023-05-01 13:55:06 -04:00
} else
writer . putInt ( 0 );
2023-03-15 08:47:22 -04:00
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 );
2023-04-30 10:20:21 -04:00
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
}
2023-03-15 08:47:22 -04:00
writer . put (( byte ) 0 );
writer . put (( byte ) 0 );
writer . put (( byte ) 0 );
2023-04-30 10:20:21 -04:00
writer . putInt ( 0 ); // NPC menu options
2023-03-15 08:47:22 -04:00
2023-08-26 11:55:18 -04:00
if ( mob . contract != null && mob . guardCaptain == null ) {
2023-03-15 08:47:22 -04:00
writer . put (( byte ) 1 );
writer . putLong ( 0 );
writer . putLong ( 0 );
2023-05-01 13:55:06 -04:00
if ( mob . contract != null )
writer . putInt ( mob . contract . getIconID ());
else
writer . putInt ( 0 ); //npc icon ID
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
} else
writer . put (( byte ) 0 );
2023-03-15 08:47:22 -04:00
2023-08-26 11:55:18 -04:00
if ( mob . guardCaptain != null ) {
2023-03-15 08:47:22 -04:00
writer . put (( byte ) 1 );
writer . putInt ( GameObjectType . PlayerCharacter . ordinal ());
writer . putInt ( 131117009 );
2023-08-26 11:55:18 -04:00
writer . putInt ( mob . guardCaptain . getObjectType (). ordinal ());
writer . putInt ( mob . guardCaptain . getObjectUUID ());
2023-03-15 08:47:22 -04:00
writer . putInt ( 8 );
2023-05-01 13:55:06 -04:00
} else
writer . put (( byte ) 0 );
2023-03-15 08:47:22 -04:00
if ( mob . isPet ()) {
writer . put (( byte ) 1 );
2023-08-27 21:04:34 -05:00
2023-09-01 14:17:07 -04:00
if ( mob . guardCaptain != null ) {
2023-08-27 21:04:34 -05:00
2023-09-01 14:17:07 -04:00
writer . putInt ( mob . guardCaptain . getObjectType (). ordinal ());
2023-08-27 21:04:34 -05:00
2023-09-01 14:17:07 -04:00
writer . putInt ( mob . guardCaptain . getObjectUUID ());
2023-03-15 08:47:22 -04:00
} else {
writer . putInt ( 0 ); //ownerType
writer . putInt ( 0 ); //ownerID
}
2023-05-01 13:55:06 -04:00
} else
writer . put (( byte ) 0 );
2023-03-15 08:47:22 -04:00
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 );
2023-08-31 08:13:37 -04:00
if ( ! mob . isAlive () && ! mob . isPet () && ! mob . isNecroPet () && ! mob . behaviourType . equals ( MobBehaviourType . SiegeEngine ) && ! mob . isPlayerGuard ()) {
2023-03-15 08:47:22 -04:00
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 );
2023-08-26 11:19:36 -04:00
} else if ( mob . behaviourType . equals ( MobBehaviourType . SiegeEngine )) {
2023-03-15 08:47:22 -04:00
writer . putInt ( 1 );
writer . putInt ( 74620179 );
2023-05-01 13:55:06 -04:00
} else
writer . putInt ( 0 );
2023-03-15 08:47:22 -04:00
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.
2023-05-01 13:55:06 -04:00
if ( ! mob . isAlive ())
writer . putInt ( 0 );
2023-03-15 08:47:22 -04:00
else {
int indexPosition = writer . position ();
writer . putInt ( 0 ); //placeholder for item cnt
int total = 0 ;
for ( Effect eff : mob . getEffects (). values ()) {
2023-05-01 13:55:06 -04:00
if ( eff . isStatic ())
continue ;
if ( ! eff . serializeForLoad ( writer ))
continue ;
2023-03-15 08:47:22 -04:00
++ total ;
}
writer . putIntAt ( total , indexPosition );
}
2023-05-01 13:55:06 -04:00
// Effects
2023-03-15 08:47:22 -04:00
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 );
}
2023-04-23 08:51:49 -04:00
2023-09-01 20:57:09 -05:00
public static Mob createMob ( int loadID , Vector3fImmutable spawn , Guild guild , Zone parent , Building building , Contract contract , String pirateName , int level , AIAgentType mobType ) {
2023-03-15 08:47:22 -04:00
2023-08-25 11:59:59 -04:00
Mob mobile = new Mob ();
mobile . dbID = MBServerStatics . NO_DB_ROW_ASSIGNED_YET ;
2023-09-01 20:57:09 -05:00
mobile . agentType = mobType ;
2023-08-31 08:03:30 -04:00
mobile . behaviourType = MobBehaviourType . None ;
2023-08-25 11:59:59 -04:00
mobile . loadID = loadID ;
2023-08-25 12:11:50 -04:00
mobile . level = ( short ) level ;
2023-08-25 11:59:59 -04:00
2023-08-25 12:12:54 -04:00
if ( guild == null || guild . isEmptyGuild ())
2023-08-25 11:59:59 -04:00
mobile . guildUUID = 0 ;
else
mobile . guildUUID = guild . getObjectUUID ();
mobile . parentZoneUUID = parent . getObjectUUID ();
2024-01-28 11:24:20 -05:00
if ( building == null )
mobile . buildingUUID = 0 ;
else
mobile . buildingUUID = building . getObjectUUID ();
2023-08-25 12:11:50 -04:00
if ( mobile . buildingUUID != 0 )
mobile . bindLoc = Vector3fImmutable . ZERO ;
else
2024-01-28 11:26:54 -05:00
mobile . bindLoc = ZoneManager . worldToLocal ( spawn , parent );
;
2023-08-25 12:11:50 -04:00
2023-08-25 11:59:59 -04:00
mobile . firstName = pirateName ;
2023-08-25 13:31:56 -04:00
if ( contract == null )
mobile . contractUUID = 0 ;
else
mobile . contractUUID = contract . getContractID ();
2023-04-29 15:40:50 -04:00
2023-09-02 19:43:40 -05:00
2023-08-31 08:03:30 -04:00
return mobile ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-08-25 14:33:43 -04:00
public static synchronized Mob createGuardMinion ( Mob guardCaptain , short level , String minionName ) {
2023-04-23 08:51:49 -04:00
2023-08-25 14:33:43 -04:00
Mob minionMobile ;
2023-04-23 08:51:49 -04:00
2023-08-25 14:33:43 -04:00
minionMobile = new Mob ();
2023-08-25 15:34:47 -04:00
minionMobile . currentID = ( -- Mob . staticID );
2023-08-25 14:33:43 -04:00
minionMobile . level = level ;
minionMobile . loadID = guardCaptain . loadID ;
minionMobile . firstName = minionName ;
minionMobile . equipmentSetID = guardCaptain . equipmentSetID ;
2023-08-26 09:41:58 -04:00
minionMobile . buildingUUID = guardCaptain . building . getObjectUUID ();
minionMobile . guildUUID = guardCaptain . guildUUID ;
2023-08-25 14:33:43 -04:00
minionMobile . runeSet = guardCaptain . runeSet ;
minionMobile . enemy = guardCaptain . enemy ;
minionMobile . notEnemy = guardCaptain . notEnemy ;
2023-04-23 08:51:49 -04:00
2023-08-25 14:33:43 -04:00
minionMobile . deathTime = System . currentTimeMillis ();
2023-08-26 11:55:18 -04:00
minionMobile . guardCaptain = guardCaptain ;
2023-10-23 00:07:48 -04:00
minionMobile . spawnDelay = ( int ) ( - 2 . 500 * guardCaptain . building . getRank () + 22 . 5 ) * 60 ;
2024-04-05 07:59:44 -04:00
minionMobile . behaviourType = mbEnums . MobBehaviourType . GuardMinion ;
2023-09-01 13:57:58 -04:00
minionMobile . agentType = AIAgentType . GUARDMINION ;
2023-08-25 14:33:43 -04:00
minionMobile . guardedCity = guardCaptain . guardedCity ;
2023-08-26 11:06:48 -04:00
minionMobile . patrolPoints = guardCaptain . building . patrolPoints ;
2023-08-25 15:14:33 -04:00
2023-08-25 14:33:43 -04:00
minionMobile . parentZoneUUID = guardCaptain . parentZoneUUID ;
2023-08-25 17:05:46 -04:00
minionMobile . bindLoc = Vector3fImmutable . ZERO ;
2023-04-23 08:51:49 -04:00
2023-08-25 14:45:47 -04:00
//grab name from minionbase.
2023-05-01 13:55:06 -04:00
2024-04-05 07:59:44 -04:00
mbEnums . MinionType minionType = mbEnums . MinionType . ContractToMinionMap . get ( guardCaptain . contract . getContractID ());
2023-04-23 08:51:49 -04:00
2023-08-25 14:33:43 -04:00
if ( minionType != null ) {
String rank ;
2023-04-23 08:51:49 -04:00
2023-08-25 14:33:43 -04:00
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 ();
2023-08-25 14:45:47 -04:00
2023-04-23 08:51:49 -04:00
}
2023-08-25 14:45:47 -04:00
// Configure and spawn minion
minionMobile . runAfterLoad ();
2023-08-25 15:37:02 -04:00
DbManager . addToCache ( minionMobile );
2023-08-25 14:45:47 -04:00
minionMobile . setLoc ( minionMobile . bindLoc );
2023-08-25 16:19:04 -04:00
minionMobile . despawn ();
2023-08-25 14:45:47 -04:00
2023-09-04 14:50:59 -04:00
guardCaptain . minions . add ( minionMobile . getObjectUUID ());
2023-04-30 16:54:39 -05:00
2023-08-25 14:33:43 -04:00
return minionMobile ;
2023-04-23 08:51:49 -04:00
}
2023-08-26 12:03:33 -04:00
public static synchronized Mob createSiegeMinion ( NPC artyCaptain , int loadID ) {
2023-04-23 08:51:49 -04:00
2023-08-26 11:55:18 -04:00
Mob siegeMinion ;
2023-04-23 08:51:49 -04:00
2023-08-26 11:55:18 -04:00
siegeMinion = new Mob ();
2023-08-28 00:46:39 -04:00
siegeMinion . currentID = ( -- Mob . staticID );
2023-04-23 08:51:49 -04:00
2023-08-26 11:55:18 -04:00
siegeMinion . level = 1 ;
siegeMinion . loadID = loadID ;
2023-08-26 12:03:33 -04:00
siegeMinion . guildUUID = artyCaptain . guildUUID ;
2023-08-26 11:55:18 -04:00
siegeMinion . equipmentSetID = 0 ;
2023-08-26 12:03:33 -04:00
siegeMinion . buildingUUID = artyCaptain . buildingUUID ;
siegeMinion . guardCaptain = artyCaptain ;
siegeMinion . parentZoneUUID = artyCaptain . parentZoneUUID ;
2023-08-26 11:55:18 -04:00
siegeMinion . behaviourType = MobBehaviourType . SiegeEngine ;
2023-09-01 13:57:58 -04:00
siegeMinion . agentType = AIAgentType . SIEGEENGINE ;
2023-08-26 11:55:18 -04:00
siegeMinion . bindLoc = Vector3fImmutable . ZERO ;
2023-10-23 00:07:48 -04:00
siegeMinion . spawnDelay = ( 60 * 15 );
2023-04-23 08:51:49 -04:00
2023-08-26 11:55:18 -04:00
siegeMinion . runAfterLoad ();
2023-08-28 00:07:40 -04:00
2023-08-26 11:55:18 -04:00
DbManager . addToCache ( siegeMinion );
siegeMinion . setLoc ( siegeMinion . bindLoc );
2023-08-26 12:03:33 -04:00
siegeMinion . despawn ();
2023-08-26 11:55:18 -04:00
2023-09-04 14:50:59 -04:00
artyCaptain . minions . add ( siegeMinion . getObjectUUID ());
2023-08-28 00:08:35 -04:00
2023-08-26 11:55:18 -04:00
return siegeMinion ;
2023-04-23 08:51:49 -04:00
}
2023-08-28 00:56:13 -04:00
public static synchronized Mob createPetMinion ( int loadID , Zone parent , PlayerCharacter petOwner , short level ) {
2023-08-26 14:52:18 -04:00
if ( petOwner == null )
return null ;
2023-08-28 00:56:13 -04:00
Mob petMinion = new Mob ();
petMinion . currentID = ( -- Mob . staticID );
2023-08-26 14:52:18 -04:00
2023-08-26 15:47:10 -04:00
petMinion . level = ( short ) ( level + 20 );
2023-08-26 14:52:18 -04:00
petMinion . loadID = loadID ;
2023-08-26 14:59:25 -04:00
petMinion . bindLoc = petOwner . getLoc ();
2023-08-26 14:52:18 -04:00
petMinion . loc = petOwner . getLoc ();
petMinion . guardCaptain = petOwner ;
petMinion . parentZoneUUID = parent . getObjectUUID ();
petMinion . walkMode = false ;
2023-08-26 15:10:14 -04:00
petMinion . healthMax = MobBase . getMobBase ( loadID ). getHealthMax () * ( petMinion . level * 0 . 5f );
2023-08-26 14:52:18 -04:00
petMinion . health . set ( petMinion . healthMax );
2023-08-26 15:00:43 -04:00
petMinion . behaviourType = MobBehaviourType . Pet1 ;
2023-09-01 13:57:58 -04:00
petMinion . agentType = AIAgentType . PET ;
2023-08-26 15:19:17 -04:00
petMinion . firstName = "" ;
petMinion . lastName = "" ;
2023-08-28 00:56:13 -04:00
2023-08-26 15:19:17 -04:00
petMinion . despawned = false ;
2023-08-26 14:52:18 -04:00
petMinion . runAfterLoad ();
DbManager . addToCache ( petMinion );
2023-08-26 14:59:25 -04:00
petMinion . setLoc ( petMinion . bindLoc );
2023-08-28 00:56:13 -04:00
2023-08-26 14:52:18 -04:00
return petMinion ;
}
2023-09-01 14:17:07 -04:00
2023-08-26 14:52:18 -04:00
public static Mob getMob ( int id ) {
if ( id == 0 )
return null ;
Mob mob = ( Mob ) DbManager . getFromCache ( GameObjectType . Mob , id );
2023-09-02 13:16:34 -04:00
2023-08-26 14:52:18 -04:00
if ( mob != null )
return mob ;
2023-09-02 13:16:34 -04:00
2023-08-26 14:52:18 -04:00
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 ;
}
2023-03-15 08:47:22 -04:00
/*
* Getters
*/
@Override
public int getDBID () {
return this . dbID ;
}
public int getLoadID () {
return loadID ;
}
2023-04-23 08:51:49 -04:00
/*
* Serialization
*/
2023-03-15 08:47:22 -04:00
@Override
public int getObjectUUID () {
return currentID ;
}
public float getSpawnRadius () {
return this . spawnRadius ;
}
public String getSpawnTimeAsString () {
2023-10-23 00:07:48 -04:00
if ( this . spawnDelay == 0 )
2023-05-01 13:55:06 -04:00
return MBServerStatics . DEFAULT_SPAWN_TIME_MS / 1000 + " seconds (Default)" ;
else
2023-10-23 00:07:48 -04:00
return this . spawnDelay + " seconds" ;
2023-03-15 08:47:22 -04:00
}
@Override
public MobBase getMobBase () {
return this . mobBase ;
}
public int getMobBaseID () {
2023-09-02 15:00:06 -04:00
return this . mobBase . getObjectUUID ();
2023-03-15 08:47:22 -04:00
}
public Vector3fImmutable getTrueBindLoc () {
return this . bindLoc ;
}
public Zone getParentZone () {
return this . parentZone ;
}
@Override
public int getGuildUUID () {
2023-05-01 13:55:06 -04:00
if ( this . guild == null )
return 0 ;
2023-03-15 08:47:22 -04:00
return this . guild . getObjectUUID ();
}
@Override
public Vector3fImmutable getBindLoc () {
2023-08-26 11:19:36 -04:00
if ( this . isPet () && ! this . behaviourType . equals ( MobBehaviourType . SiegeEngine ))
2023-09-01 14:17:07 -04:00
return this . guardCaptain != null ? this . guardCaptain . getLoc () : this . getLoc ();
2023-08-26 11:19:36 -04:00
else
return this . bindLoc ;
2023-03-15 08:47:22 -04:00
}
public void calculateModifiedStats () {
float strVal = this . mobBase . getMobBaseStats (). getBaseStr ();
float dexVal = this . mobBase . getMobBaseStats (). getBaseDex ();
2024-04-02 13:09:24 -04:00
float conVal = this . mobBase . getMobBaseStats (). getBaseCon ();
2023-03-15 08:47:22 -04:00
float intVal = this . mobBase . getMobBaseStats (). getBaseInt ();
float spiVal = this . mobBase . getMobBaseStats (). getBaseSpi ();
// TODO modify for equipment
if ( this . bonuses != null ) {
// modify for effects
2024-04-01 09:19:37 -04:00
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 );
2023-03-15 08:47:22 -04:00
// apply dex penalty for armor
// modify percent amounts. DO THIS LAST!
2024-04-01 09:19:37 -04:00
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 ));
2023-03-15 08:47:22 -04:00
}
// 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
2024-04-01 09:19:37 -04:00
bonus *= ( 1 + this . bonuses . getFloatPercentAll ( ModType . Speed , SourceType . None ));
2023-03-15 08:47:22 -04:00
2023-08-31 08:13:37 -04:00
if ( this . isPlayerGuard ())
2023-05-01 13:55:06 -04:00
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 ;
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
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 ;
}
2023-03-15 08:47:22 -04:00
//return combat speeds
2023-04-22 07:52:05 -04:00
//not combat return normal speeds
2023-05-01 13:55:06 -04:00
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 ;
}
2023-04-22 07:52:05 -04:00
else if ( this . isWalk ()) {
2023-05-01 13:55:06 -04:00
if ( this . mobBase . getWalk () <= 0 )
return MBServerStatics . MOB_SPEED_WALK * bonus ;
2023-04-22 07:52:05 -04:00
return this . mobBase . getWalk () * bonus ;
} else {
2023-05-01 13:55:06 -04:00
if ( this . mobBase . getRun () <= 0 )
return MBServerStatics . MOB_SPEED_RUN * bonus ;
2023-04-22 07:52:05 -04:00
return this . mobBase . getRun () * bonus ;
2023-03-15 08:47:22 -04:00
}
}
@Override
public float getPassiveChance ( String type , int AttackerLevel , boolean fromCombat ) {
//TODO add this later for dodge
return 0f ;
}
2023-04-23 08:51:49 -04:00
/*
* Database
*/
2023-03-15 08:47:22 -04:00
/**
* @ Kill this Character
*/
@Override
public void killCharacter ( AbstractCharacter attacker ) {
this . stopMovement ( this . getMovementLoc ());
2023-05-01 13:55:06 -04:00
if ( attacker != null )
if ( attacker . getObjectType () == GameObjectType . PlayerCharacter ) {
Group g = GroupManager . getGroup (( PlayerCharacter ) attacker );
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
// Give XP, now handled inside the Experience Object
2023-08-31 08:13:37 -04:00
if ( ! this . isPet () && ! this . isNecroPet () && ! ( this . agentType . equals ( AIAgentType . PET )) && ! this . isPlayerGuard ())
2023-05-01 13:55:06 -04:00
Experience . doExperience (( PlayerCharacter ) attacker , this , g );
} else if ( attacker . getObjectType (). equals ( GameObjectType . Mob )) {
Mob mobAttacker = ( Mob ) attacker ;
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
if ( mobAttacker . isPet ()) {
2023-03-15 08:47:22 -04:00
2023-08-27 21:04:34 -05:00
PlayerCharacter owner = ( PlayerCharacter ) mobAttacker . guardCaptain ;
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
if ( owner != null )
2023-08-31 08:13:37 -04:00
if ( ! this . isPet () && ! this . isNecroPet () && ! ( this . agentType . equals ( AIAgentType . PET )) && ! this . isPlayerGuard ()) {
2023-05-01 13:55:06 -04:00
Group g = GroupManager . getGroup ( owner );
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
// Give XP, now handled inside the Experience Object
Experience . doExperience ( owner , this , g );
}
}
2023-03-15 08:47:22 -04:00
}
killCleanup ();
}
public void updateLocation () {
2023-05-01 13:55:06 -04:00
if ( ! this . isMoving ())
return ;
2023-03-15 08:47:22 -04:00
2024-04-01 09:19:37 -04:00
if ( this . isAlive () == false || this . getBonuses (). getBool ( ModType . Stunned , SourceType . None ) || this . getBonuses (). getBool ( ModType . CannotMove , SourceType . None )) {
2023-03-15 08:47:22 -04:00
//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 {
2023-07-22 08:26:55 -05:00
//resync corpses
2023-08-30 15:47:08 -04:00
2023-08-26 11:19:36 -04:00
if ( this . behaviourType . equals ( MobBehaviourType . SiegeEngine )) {
2023-03-15 08:47:22 -04:00
this . deathTime = System . currentTimeMillis ();
2023-08-30 15:47:08 -04:00
2023-03-15 08:47:22 -04:00
try {
this . clearEffects ();
} catch ( Exception e ) {
Logger . error ( e . getMessage ());
}
2023-08-30 15:47:08 -04:00
2023-08-08 13:36:46 -05:00
this . setCombatTarget ( null );
2023-03-15 08:47:22 -04:00
this . hasLoot = false ;
this . playerAgroMap . clear ();
2024-04-05 07:59:44 -04:00
if ( this . behaviourType . ordinal () == mbEnums . MobBehaviourType . GuardMinion . ordinal ())
2023-10-23 00:07:48 -04:00
this . spawnDelay = ( int ) ( - 2 . 500 * this . guardCaptain . building . getRank () + 22 . 5 ) * 60 ;
2023-08-18 11:33:27 -04:00
2023-03-15 08:47:22 -04:00
if ( this . isPet ()) {
2023-08-27 21:04:34 -05:00
PlayerCharacter petOwner = ( PlayerCharacter ) this . guardCaptain ;
2023-03-15 08:47:22 -04:00
if ( petOwner != null ) {
2023-08-27 21:04:34 -05:00
this . guardCaptain = null ;
2023-03-15 08:47:22 -04:00
petOwner . setPet ( null );
PetMsg petMsg = new PetMsg ( 5 , null );
2023-08-27 21:04:34 -05:00
dispatch = Dispatch . borrow (( PlayerCharacter ) this . guardCaptain , petMsg );
2024-05-12 13:14:42 -04:00
DispatchManager . dispatchMsgDispatch ( dispatch , mbEnums . DispatchChannel . PRIMARY );
2023-03-15 08:47:22 -04:00
}
}
} else if ( this . isPet () || this . isNecroPet ()) {
2023-08-08 13:36:46 -05:00
this . setCombatTarget ( null );
2023-03-15 08:47:22 -04:00
this . hasLoot = false ;
2023-10-09 06:16:25 -04:00
ZoneManager . seaFloor . zoneMobSet . remove ( this );
2023-08-30 15:47:08 -04:00
2023-03-15 08:47:22 -04:00
try {
this . clearEffects ();
} catch ( Exception e ) {
Logger . error ( e . getMessage ());
}
2023-08-30 15:47:08 -04:00
2023-03-15 08:47:22 -04:00
this . playerAgroMap . clear ();
WorldGrid . RemoveWorldObject ( this );
DbManager . removeFromCache ( this );
2023-08-27 21:04:34 -05:00
PlayerCharacter petOwner = ( PlayerCharacter ) this . guardCaptain ;
2023-03-15 08:47:22 -04:00
if ( petOwner != null ) {
2023-08-27 21:04:34 -05:00
this . guardCaptain = null ;
2023-03-15 08:47:22 -04:00
petOwner . setPet ( null );
PetMsg petMsg = new PetMsg ( 5 , null );
dispatch = Dispatch . borrow ( petOwner , petMsg );
2024-05-12 13:14:42 -04:00
DispatchManager . dispatchMsgDispatch ( dispatch , mbEnums . DispatchChannel . PRIMARY );
2023-03-15 08:47:22 -04:00
}
} else {
//cleanup effects
2023-08-30 15:47:08 -04:00
2023-03-15 08:47:22 -04:00
playerAgroMap . clear ();
2024-03-18 09:38:33 -04:00
if ( ! this . isPlayerGuard () && this . charItemManager . equipped != null )
2023-08-21 19:36:46 -05:00
LootManager . GenerateEquipmentDrop ( this );
2023-03-15 08:47:22 -04:00
}
try {
this . clearEffects ();
} catch ( Exception e ) {
Logger . error ( e . getMessage ());
}
this . combat = false ;
this . walkMode = true ;
2023-08-08 13:36:46 -05:00
this . setCombatTarget ( null );
2023-07-22 08:26:55 -05:00
2023-03-15 08:47:22 -04:00
this . hasLoot = this . charItemManager . getInventoryCount () > 0 ;
} catch ( Exception e ) {
Logger . error ( e );
}
2023-07-25 21:27:54 -05:00
this . updateLocation ();
2023-03-15 08:47:22 -04:00
}
public void respawn () {
2023-08-18 11:33:27 -04:00
2023-07-22 08:26:55 -05:00
this . despawned = false ;
2023-03-15 08:47:22 -04:00
this . setCombatTarget ( null );
this . setHealth ( this . healthMax );
this . stamina . set ( this . staminaMax );
this . mana . set ( this . manaMax );
this . combat = false ;
this . walkMode = true ;
2023-08-08 13:36:46 -05:00
this . setCombatTarget ( null );
2023-03-15 08:47:22 -04:00
this . isAlive . set ( true );
2023-04-14 19:20:12 -05:00
this . deathTime = 0 ;
2023-03-30 11:07:57 -04:00
this . lastBindLoc = this . bindLoc ;
2023-07-22 08:26:55 -05:00
this . setLoc ( this . lastBindLoc );
2023-03-15 08:47:22 -04:00
this . stopMovement ( this . lastBindLoc );
2023-08-18 11:33:27 -04:00
2023-09-01 21:39:34 -05:00
NPCManager . applyMobbaseEffects ( this );
2023-08-18 11:33:27 -04:00
2023-03-15 08:47:22 -04:00
this . recalculateStats ();
this . setHealth ( this . healthMax );
2023-08-18 11:33:27 -04:00
2023-08-26 12:21:47 -04:00
if ( this . building == null && this . guardCaptain != null && (( Mob ) this . guardCaptain ). behaviourType . equals ( MobBehaviourType . GuardCaptain ))
2023-08-26 11:55:18 -04:00
this . building = this . guardCaptain . building ;
2023-08-18 11:33:27 -04:00
2023-09-01 12:55:33 -04:00
this . loadInventory ();
2023-08-18 11:33:27 -04:00
2023-07-25 21:27:54 -05:00
this . updateLocation ();
2023-03-15 08:47:22 -04:00
}
public void despawn () {
this . despawned = true ;
WorldGrid . RemoveWorldObject ( this );
this . charItemManager . clearInventory ();
}
@Override
public boolean canBeLooted () {
return ! this . isAlive ();
}
public int getTypeMasks () {
2023-05-01 13:55:06 -04:00
if ( this . mobBase == null )
return 0 ;
2023-03-15 08:47:22 -04:00
return this . mobBase . getTypeMasks ();
}
/**
* Clears and sets the inventory of the Mob. Must be called every time the
* mob is spawned or respawned.
*/
2023-04-07 21:41:03 -05:00
public void loadInventory () {
2023-03-15 08:47:22 -04:00
2023-05-01 13:55:06 -04:00
if ( ! MBServerStatics . ENABLE_MOB_LOOT )
return ;
2023-03-15 08:47:22 -04:00
this . charItemManager . clearInventory ();
this . charItemManager . clearEquip ();
2024-03-18 13:29:09 -04:00
// Reload equipment set
this . charItemManager . equipped = MobBase . loadEquipmentSet ( this . equipmentSetID );
2023-09-01 12:55:33 -04:00
// Only generate loot for mobiles
if ( ! this . agentType . equals ( AIAgentType . MOBILE ))
2023-05-01 13:55:06 -04:00
return ;
2023-03-15 08:47:22 -04:00
2023-08-21 19:36:46 -05:00
LootManager . GenerateMobLoot ( this );
2023-03-15 08:47:22 -04:00
}
@Override
public void updateDatabase () {
}
public void refresh () {
2023-05-01 13:55:06 -04:00
if ( this . isAlive ())
WorldGrid . updateObject ( this );
2023-03-15 08:47:22 -04:00
}
public void recalculateStats () {
2024-03-28 03:42:29 -04:00
2024-03-24 09:42:27 -04:00
if ( this . isPlayerGuard ()) {
2023-09-09 19:23:46 -05:00
NPCManager . setMaxHealthForGuard ( this );
NPCManager . setAttackRatingForGuard ( this );
NPCManager . setDefenseForGuard ( this );
NPCManager . setDamageAndSpeedForGuard ( this );
2023-09-09 19:41:32 -05:00
NPCManager . applyGuardStanceModifiers ( this );
2024-03-24 09:42:27 -04:00
} else {
2024-03-28 03:42:29 -04:00
AbstractCharacter . calculateAtrDefenseDamage ( this );
calculateMaxHealthManaStamina ();
}
calculateModifiedStats ();
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
if ( this . isSiege ())
2023-09-09 19:23:46 -05:00
this . healthMax = 10000 ;
2024-03-28 03:42:29 -04:00
2023-03-15 08:47:22 -04:00
Resists . calculateResists ( this );
}
public void calculateMaxHealthManaStamina () {
2024-03-24 09:42:27 -04:00
float h ;
float m ;
float s ;
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
h = this . mobBase . getHealthMax ();
if ( this . isPet ()) {
h = this . level * 0 . 5f * 120 ;
}
m = this . statSpiCurrent ;
s = this . statConCurrent ;
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
// Apply any bonuses from runes and effects
2023-04-22 07:52:05 -04:00
2024-03-24 09:42:27 -04:00
if ( this . bonuses != null ) {
2024-04-01 09:19:37 -04:00
h += this . bonuses . getFloat ( ModType . HealthFull , SourceType . None );
m += this . bonuses . getFloat ( ModType . ManaFull , SourceType . None );
s += this . bonuses . getFloat ( ModType . StaminaFull , SourceType . None );
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
//apply effects percent modifiers. DO THIS LAST!
2023-04-22 07:52:05 -04:00
2024-04-01 09:19:37 -04:00
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 ));
2024-03-24 09:42:27 -04:00
}
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
// Set max health, mana and stamina
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
if ( h > 0 )
this . healthMax = h ;
else
this . healthMax = 1 ;
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
if ( m > - 1 )
this . manaMax = m ;
else
this . manaMax = 0 ;
2023-04-22 07:52:05 -04:00
2024-03-24 09:42:27 -04:00
if ( s > - 1 )
this . staminaMax = s ;
else
this . staminaMax = 0 ;
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
// Update health, mana and stamina if needed
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
if ( this . getHealth () > this . healthMax )
this . setHealth ( this . healthMax );
2023-03-15 08:47:22 -04:00
2024-03-24 09:42:27 -04:00
if ( this . mana . get () > this . manaMax )
this . mana . set ( this . manaMax );
2023-04-22 07:52:05 -04:00
2024-03-24 09:42:27 -04:00
if ( this . stamina . get () > this . staminaMax )
this . stamina . set ( staminaMax );
2023-03-15 08:47:22 -04:00
}
@Override
public void runAfterLoad () {
2022-04-30 09:41:17 -04:00
2023-09-01 14:05:50 -04:00
this . setObjectTypeMask ( MBServerStatics . MASK_MOB | this . getTypeMasks ());
2023-08-23 13:19:57 -04:00
if ( ConfigManager . serverType . equals ( ServerType . LOGINSERVER ))
return ;
2023-08-25 16:19:04 -04:00
this . mobBase = MobBase . getMobBase ( loadID );
2023-08-26 10:11:13 -04:00
2023-09-01 14:05:50 -04:00
this . building = BuildingManager . getBuilding ( this . buildingUUID );
2023-08-26 10:11:13 -04:00
2023-09-01 13:57:58 -04:00
// Configure AI related values
2023-08-25 13:07:47 -04:00
2023-09-01 13:57:58 -04:00
switch ( this . behaviourType ) {
case GuardCaptain :
this . agentType = AIAgentType . GUARDCAPTAIN ;
2023-10-23 00:07:48 -04:00
this . spawnDelay = 600 ;
2024-02-26 15:49:45 -05:00
if ( this . building == null )
2024-02-26 15:52:49 -05:00
Logger . error ( "Captain : " + this . getObjectUUID () + " missing building " + this . buildingUUID );
2023-09-01 13:57:58 -04:00
this . guardedCity = ZoneManager . getCityAtLocation ( this . building . getLoc ());
break ;
case GuardWallArcher :
this . agentType = AIAgentType . GUARDWALLARCHER ;
2023-10-23 00:07:48 -04:00
this . spawnDelay = 450 ;
2024-02-26 15:49:45 -05:00
if ( this . building == null )
2024-02-26 15:52:49 -05:00
Logger . error ( "Wall Archer : " + this . getObjectUUID () + " missing building " + this . buildingUUID );
2023-09-01 13:57:58 -04:00
this . guardedCity = ZoneManager . getCityAtLocation ( this . building . getLoc ());
break ;
2023-08-25 15:23:02 -04:00
}
2023-08-31 08:03:30 -04:00
// Default to the mobbase for AI if nothing is in mob field to override.
2023-08-25 16:19:04 -04:00
if ( this . behaviourType == null || this . behaviourType . equals ( MobBehaviourType . None ))
this . behaviourType = this . getMobBase (). fsm ;
2023-08-25 16:49:26 -04:00
if ( this . behaviourType == null )
this . behaviourType = MobBehaviourType . None ;
2023-09-01 13:57:58 -04:00
if ( this . contractUUID == 0 )
this . contract = null ;
else
this . contract = DbManager . ContractQueries . GET_CONTRACT ( this . contractUUID );
// Setup equipset for contract
if ( this . contract != null )
2024-04-05 09:18:12 -04:00
this . equipmentSetID = this . contract . equipmentSet ;
2023-09-01 13:57:58 -04:00
2023-09-01 14:07:00 -04:00
// Mobiles default to the building guild.
2023-08-23 12:24:43 -04:00
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 ();
2023-08-25 17:05:46 -04:00
if ( this . contract != null )
2023-08-25 15:29:42 -04:00
if ( this . lastName . isEmpty ())
this . lastName = this . getContract (). getName ();
2023-08-23 12:24:43 -04:00
2023-08-25 15:19:25 -04:00
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 );
2023-08-23 13:19:57 -04:00
2023-08-29 20:18:05 -05:00
// Don't override level for guard minions or pets
2023-08-25 15:29:42 -04:00
2023-08-25 15:19:25 -04:00
if ( this . contract == null )
2023-09-01 13:57:58 -04:00
if ( ! this . agentType . equals ( AIAgentType . GUARDMINION ) && ! this . agentType . equals ( AIAgentType . PET ))
2023-08-25 15:29:42 -04:00
this . level = ( short ) this . mobBase . getLevel ();
2023-08-23 13:19:57 -04:00
2023-08-25 15:19:25 -04:00
//set bonuses
2023-08-23 13:19:57 -04:00
2023-08-25 15:19:25 -04:00
this . bonuses = new PlayerBonuses ( this );
2023-08-23 13:19:57 -04:00
2023-08-25 15:19:25 -04:00
//TODO set these correctly later
2023-08-27 20:50:13 -05:00
this . rangeHandOne = this . mobBase . getAttackRange ();
2023-08-25 15:19:25 -04:00
this . rangeHandTwo = - 1 ;
2023-09-09 20:40:52 -05:00
this . minDamageHandOne = ( int ) this . mobBase . getDamageMin ();
this . maxDamageHandOne = ( int ) this . mobBase . getDamageMax ();
2023-08-27 20:50:13 -05:00
this . minDamageHandTwo = 0 ;
this . maxDamageHandTwo = 0 ;
2023-09-09 20:40:52 -05:00
this . atrHandOne = this . mobBase . getAttackRating ();
2023-08-25 15:19:25 -04:00
this . defenseRating = ( short ) this . mobBase . getDefenseRating ();
this . isActive = true ;
2023-08-23 12:38:46 -04:00
// Configure parent zone adding this NPC to the
// zone collection
2023-08-25 11:59:59 -04:00
this . parentZone = ZoneManager . getZoneByUUID ( this . parentZoneUUID );
2023-08-23 12:38:46 -04:00
this . parentZone . zoneMobSet . remove ( this );
this . parentZone . zoneMobSet . add ( this );
// Handle Mobiles within buildings
2023-08-26 15:21:50 -04:00
if ( this . building == null ) {
// Do not adjust a pet's bindloc.
2023-08-29 20:18:05 -05:00
if ( ! this . agentType . equals ( AIAgentType . PET ))
2023-08-26 15:21:50 -04:00
this . bindLoc = this . parentZone . getLoc (). add ( this . bindLoc );
} else {
2023-08-23 12:38:46 -04:00
// Mobiles inside buildings are offset from it not the zone
2023-08-25 16:19:04 -04:00
// with the exceptions being mobiles
// with a contract.
2023-08-23 12:38:46 -04:00
2023-08-26 12:35:14 -04:00
if ( this . contract != null || this . behaviourType . equals ( MobBehaviourType . SiegeEngine ))
2023-08-23 12:38:46 -04:00
NPCManager . slotCharacterInBuilding ( this );
2023-08-26 09:41:58 -04:00
else
this . bindLoc = building . getLoc (). add ( bindLoc );
2023-08-23 12:38:46 -04:00
}
2023-08-23 12:24:43 -04:00
2023-08-25 16:19:04 -04:00
// Setup location for this Mobile
2023-09-13 21:07:57 -05:00
this . setLoc ( bindLoc );
2023-08-25 16:19:04 -04:00
this . endLoc = new Vector3fImmutable ( bindLoc );
2023-08-20 16:38:10 -04:00
// Initialize inventory
2023-08-25 12:27:58 -04:00
this . charItemManager . load ();
2023-08-21 19:48:44 -05:00
this . loadInventory ();
2023-08-23 12:48:34 -04:00
2023-08-25 16:19:04 -04:00
if ( this . equipmentSetID != 0 )
2024-03-18 09:38:33 -04:00
this . charItemManager . equipped = MobBase . loadEquipmentSet ( this . equipmentSetID );
2023-08-08 10:29:01 -04:00
2023-04-09 13:55:46 -04:00
// Combine mobbase and mob aggro arrays into one bitvector
2023-07-15 21:57:27 -05:00
//skip for pets
2023-08-08 10:29:01 -04:00
2023-08-25 16:19:04 -04:00
if ( this . isPet () == false && this . isNecroPet () == false ) {
2023-07-15 21:57:27 -05:00
if ( this . getMobBase (). notEnemy . size () > 0 )
this . notEnemy . addAll ( this . getMobBase (). notEnemy );
2023-04-22 07:52:05 -04:00
2023-07-15 21:57:27 -05:00
if ( this . getMobBase (). enemy . size () > 0 )
this . enemy . addAll ( this . getMobBase (). enemy );
}
2023-10-17 15:09:42 -04:00
// Load skills, powers and effects
2023-09-01 21:39:34 -05:00
NPCManager . applyMobbaseEffects ( this );
NPCManager . applyEquipmentResists ( this );
2023-09-02 20:39:36 -05:00
NPCManager . applyMobbaseSkill ( this );
2023-10-17 15:09:42 -04:00
NPCManager . applyRuneSkills ( this , this . getMobBaseID ());
2023-09-01 21:34:48 -05:00
this . recalculateStats ();
2023-09-01 14:17:07 -04:00
this . setHealth ( this . healthMax );
2022-04-30 09:41:17 -04:00
2023-09-01 14:17:07 -04:00
// Set bounds for this mobile
2023-04-22 07:52:05 -04:00
2023-09-01 14:17:07 -04:00
Bounds mobBounds = Bounds . borrow ();
mobBounds . setBounds ( this . getLoc ());
this . setBounds ( mobBounds );
2023-08-08 10:29:01 -04:00
2023-09-01 14:17:07 -04:00
//assign 5 random patrol points for regular mobs
2023-04-22 07:52:05 -04:00
2023-09-01 14:17:07 -04:00
if ( this . agentType . equals ( AIAgentType . MOBILE ))
NPCManager . AssignPatrolPoints ( this );
2023-08-27 23:55:02 -04:00
2023-10-17 15:09:42 -04:00
// Load minions for guard captain.
if ( this . agentType . equals ( AIAgentType . GUARDCAPTAIN ))
DbManager . MobQueries . LOAD_GUARD_MINIONS ( this );
2023-09-01 14:17:07 -04:00
this . deathTime = 0 ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
@Override
protected ConcurrentHashMap < Integer , CharacterPower > initializePowers () {
return new ConcurrentHashMap <> ( MBServerStatics . CHM_INIT_CAP , MBServerStatics . CHM_LOAD , MBServerStatics . CHM_THREAD_LOW );
}
2022-04-30 09:41:17 -04:00
2023-09-02 15:57:23 -04:00
public boolean canSee ( AbstractCharacter target ) {
return this . mobBase . getSeeInvis () >= target . hidden ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public int getBuildingID () {
2023-08-21 17:20:57 -04:00
return buildingUUID ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public void setBuildingID ( int buildingID ) {
2023-08-21 17:20:57 -04:00
this . buildingUUID = buildingID ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public boolean isSiege () {
2023-08-26 11:19:36 -04:00
return this . behaviourType . equals ( MobBehaviourType . SiegeEngine );
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-08-26 11:55:18 -04:00
public void setGuardCaptain ( AbstractCharacter guardCaptain ) {
this . guardCaptain = guardCaptain ;
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public boolean isNecroPet () {
return this . mobBase . isNecroPet ();
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public void handleDirectAggro ( AbstractCharacter ac ) {
2022-04-30 09:41:17 -04:00
2023-04-29 15:40:50 -04:00
if ( ! ac . getObjectType (). equals ( GameObjectType . PlayerCharacter ))
return ;
2022-04-30 09:41:17 -04:00
2023-10-17 15:09:42 -04:00
if ( this . getCombatTarget () == null )
2023-08-08 13:36:46 -05:00
this . setCombatTarget ( ac );
2023-10-17 15:09:42 -04:00
2023-03-15 08:47:22 -04:00
}
2022-04-30 09:41:17 -04:00
2023-03-15 08:47:22 -04:00
public void setRank ( int newRank ) {
DbManager . MobQueries . SET_PROPERTY ( this , "mob_level" , newRank );
this . level = ( short ) newRank ;
2023-09-09 20:37:47 -05:00
this . recalculateStats ();
this . setHealth ( this . healthMax );
2023-03-15 08:47:22 -04:00
}
public boolean isRanking () {
return this . upgradeDateTime != null ;
}
2024-03-28 22:15:48 -05:00
public long getNextAttackTime () {
return nextAttackTime ;
2023-03-15 08:47:22 -04:00
}
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 () {
2023-08-31 08:13:37 -04:00
2023-09-01 12:38:23 -04:00
return EnumSet . of ( AIAgentType . GUARDCAPTAIN , AIAgentType . GUARDMINION , AIAgentType . GUARDWALLARCHER ). contains ( this . agentType );
2023-03-15 08:47:22 -04:00
}
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 () {
2023-04-30 16:05:48 -05:00
return firstName + " " + lastName ;
2023-03-15 08:47:22 -04:00
}
public void processUpgradeMob ( PlayerCharacter player ) {
lock . writeLock (). lock ();
try {
// Cannot upgrade an npc not within a building
2023-05-01 13:55:06 -04:00
if ( building == null )
return ;
2023-03-15 08:47:22 -04:00
// Cannot upgrade an npc at max rank
2023-05-01 13:55:06 -04:00
if ( this . getRank () == 7 )
return ;
2023-03-15 08:47:22 -04:00
// Cannot upgrade an npc who is currently ranking
2023-05-01 13:55:06 -04:00
if ( this . isRanking ())
return ;
2023-03-15 08:47:22 -04:00
int rankCost = Mob . getUpgradeCost ( this );
// SEND NOT ENOUGH GOLD ERROR
if ( rankCost > building . getStrongboxValue ()) {
sendErrorPopup ( player , 127 );
return ;
}
try {
2023-05-01 13:55:06 -04:00
if ( ! building . transferGold ( - rankCost , false ))
return ;
2023-03-15 08:47:22 -04:00
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 ();
}
}
2023-10-23 00:07:48 -04:00
2024-03-24 09:42:27 -04:00
public Boolean isGuard () {
2024-01-24 19:03:33 -06:00
2024-03-24 09:42:27 -04:00
switch ( this . behaviourType ) {
2024-01-24 19:03:33 -06:00
case GuardMinion :
case GuardCaptain :
case GuardWallArcher :
case HamletGuard :
case SimpleStandingGuard :
return true ;
}
return false ;
}
2023-10-23 00:07:48 -04:00
@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 );
}
2023-08-26 13:53:32 -04:00
}