/* Copyright (C) 2007 This File is part of Core3. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Linking Engine3 statically or dynamically with other modules is making a combined work based on Engine3. Thus, the terms and conditions of the GNU Lesser General Public License cover the whole combination. In addition, as a special exception, the copyright holders of Engine3 give you permission to combine Engine3 program with free software programs or libraries that are released under the GNU LGPL and with code included in the standard release of Core3 under the GNU LGPL license (or modified versions of such code, with unchanged license). You may copy and distribute such a system following the terms of the GNU LGPL for Engine3 and the licenses of the other code concerned, provided that you include the source code of that other code when and as the GNU LGPL requires distribution of source code. Note that people who make modified versions of Engine3 are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU Lesser General Public License gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception. */ package server.zone.objects.creature; import engine.lua.LuaObject; import system.util.Vector; import system.lang.Time; import server.zone.Zone; import system.util.SortedVector; import server.zone.objects.creature.CreatureObject; import server.zone.objects.creature.events.AiThinkEvent; import server.zone.objects.creature.events.AiMoveEvent; import server.zone.objects.creature.events.AiWaitEvent; import server.zone.objects.creature.events.AiAwarenessEvent; import system.lang.ref.Reference; import server.zone.packets.scene.AttributeListMessage; import server.zone.objects.creature.CreatureObject; import server.zone.objects.tangible.weapon.WeaponObject; include server.zone.objects.creature.PatrolPointsVector; include server.zone.objects.creature.PatrolPoint; include server.zone.objects.scene.WorldCoordinates; import engine.core.ManagedObject; import server.zone.objects.tangible.TangibleObject; import server.zone.objects.tangible.weapon.WeaponObject; import server.zone.templates.SharedObjectTemplate; import server.zone.templates.mobile.CreatureTemplate; import server.zone.objects.creature.events.DespawnCreatureOnPlayerDissappear; include server.zone.objects.creature.variables.CreatureAttackMap; import engine.util.u3d.Coordinate; import server.zone.objects.creature.commands.QueueCommand; import engine.util.Observable; import server.zone.objects.player.PlayerObject; include server.zone.objects.player.FactionStatus; include system.thread.Mutex; include server.zone.managers.loot.lootgroup.LootGroupCollection; include server.zone.objects.scene.components.AiInterfaceComponent; include server.zone.objects.creature.variables.CreatureTemplateReference; include system.thread.ReadWriteLock; include server.zone.objects.creature.variables.CurrentFoundPath; class AiAgent extends CreatureObject { protected transient AiThinkEvent thinkEvent; protected transient AiMoveEvent moveEvent; protected transient AiWaitEvent waitEvent; protected transient AiAwarenessEvent awarenessEvent; @dereferenced protected transient ReadWriteLock despawnMutex; @dereferenced protected transient Vector aiInterfaceComponents; @dereferenced protected Vector skillCommands; @dereferenced protected PatrolPointsVector patrolPoints; @dereferenced protected PatrolPoint homeLocation; @dereferenced protected PatrolPoint nextStepPosition; protected transient CurrentFoundPath currentFoundPath; protected transient SceneObject targetCellObject; @dereferenced protected Vector weapons; @dereferenced protected Vector camouflagedObjects; @dereferenced protected CreatureTemplateReference npcTemplate; protected boolean baby; //DEBUG protected boolean showNextMovementPosition; @dereferenced protected Vector movementMarkers; protected boolean despawnOnNoPlayerInRange; //@weakReference protected SceneObject followObject; protected unsigned int followState; @dereferenced protected transient Mutex targetMutex; protected boolean fleeing; @dereferenced protected transient Time lastDamageReceived; protected float respawnTimer; protected int numberOfPlayersInRange; protected boolean loadedOutfit; protected transient DespawnCreatureOnPlayerDissappear despawnEvent; public static final int UPDATEMOVEMENTINTERVAL = 500; // msec public static final unsigned int OBLIVIOUS = 0; public static final unsigned int WATCHING = 1; public static final unsigned int STALKING = 2; public static final unsigned int FOLLOWING = 3; public static final unsigned int PATROLING = 4; public AiAgent() { baby = false; followState = OBLIVIOUS; respawnTimer = 0; showNextMovementPosition = true; despawnOnNoPlayerInRange = true; numberOfPlayersInRange = 0; loadedOutfit = false; Logger.setLoggingName("AiAgent"); Logger.setLogging(false); Logger.setGlobalLogging(true); fleeing = false; baby = false; } /** * Initializes the transient members of SceneObject, must call the inherited object method first. * @pre {transient members are not initialized } * @post { transient members are initialized } */ public native void initializeTransientMembers(); public void finalize() { //Logger.info("deleting from ram", true); } public abstract native void activateRecovery(); public abstract native void activateMovementEvent(); public abstract native void activateWaitEvent(); public native void activateAwarenessEvent(CreatureObject target); public native boolean tryRetreat(); public native void doRecovery(); public native void doAttack(); public native void doMovement(); public native void setLevel(int lvl); /** * Sends the CREO baseline messages of this object to the specified player * @pre { this object is locked } * @post { this object is locked, player received the baseline messages } * @param player SceneObject that will receive the baselines */ public native void sendBaselinesTo(SceneObject player); public native int calculateAttackMinDamage(int level); public native int calculateAttackMaxDamage(int level); public native float calculateAttackSpeed(int level); public abstract native boolean isCamouflaged(CreatureObject target) { return false; } public native boolean isScentMasked(CreatureObject target); public native boolean isConcealed(CreatureObject target); protected native boolean findNextPosition(float maxDistance, WorldCoordinates nextPosition); @local public native void doAwarenessCheck(@dereferenced Coordinate start, unsigned long time, CreatureObject target); /** * Handles the radial selection sent by the client, must be overriden by inherited objects * @pre { this object is locked, player is locked } * @post { this object is locked, player is locked } * @param player CreatureObject that selected the option * @param selectedID selected menu id * @returns 0 if successfull */ public native int handleObjectMenuSelect(CreatureObject player, byte selectedID); public native void checkNewAngle(); public native void fillAttributeList(AttributeListMessage msg, CreatureObject object); public native void setNextPosition(float x, float z, float y, SceneObject cell = null); public native void notifyPositionUpdate(QuadTreeEntry entry); @local public native void updateCurrentPosition(PatrolPoint point); @local public native void broadcastNextPositionUpdate(PatrolPoint point); public void clearPatrolPoints() { synchronized (targetMutex) { patrolPoints.removeAll(); } } @local public native void notifyInsert(QuadTreeEntry entry); @local public native void notifyDissapear(QuadTreeEntry entry); /** * Reads and sets the template data from a SharedTangibleObjectTemplate LuaObject * @pre { templateData is a valid pointer } * @post { TangibleObject members are initialized } * @param templateData templateData points to the SharedTangibleObjectTemplate LuaObject that is used to initialize the TangibleObject members */ @local public native void loadTemplateData(SharedObjectTemplate templateData); @local public native void loadTemplateData(CreatureTemplate templateData); /** * Inflicts damage into the object * @pre { this object is locked } * @post { this object is locked } * @return unused for now */ public native int inflictDamage(TangibleObject attacker, int damageType, float damage, boolean destroy, boolean notifyClient = true); public native int inflictDamage(TangibleObject attacker, int damageType, float damage, boolean destroy, final string xp, boolean notifyClient = true); /** * sends the conversation notification * @pre {this locked, player locked } * @post { this locked, player locked } */ public native void sendConversationStartTo(SceneObject player); /** * sends the default conversation list * @pre {this locked, player locked } * @post {this locked, player locked } */ public native void sendDefaultConversationTo(SceneObject player); /** * sends the conversation list * @pre {this locked, player locked } * @post { this locked, player locked } */ public native void selectConversationOption(int option, SceneObject obj); /** * Is called when this object is destroyed * @pre { this, attacker locked } * @post { this, attacker locked } */ public native int notifyObjectDestructionObservers(TangibleObject attacker, int condition); /** * Is called when an object is talked to * @pre { this, converser locked } * @post {this, converser locked } */ public native int notifyConverseObservers(CreatureObject converser); public native int notifyAttack(Observable observable); public native int notifyCallForHelp(Observable observable, ManagedObject arg1); public void destroyObjectFromWorld(boolean sendSelfDestroy) { super.destroyObjectFromWorld(sendSelfDestroy); if (moveEvent != null) { moveEvent.clearCreatureObject(); moveEvent = null; } } public native void activatePostureRecovery(); public native void queueDizzyFallEvent(); /** * Cleares the combat state * @pre { this object is locked } * @post { this object is locked, this object is not in a combat state } * @param clearDefenders if true the defender vector willl be emptied */ public native void clearCombatState(boolean clearDefenders = true); /** * Sets the active defender * @pre { this object is locked } * @post { this object is locked, defender is active } * @param defender SceneObject to set as the active defender */ public native void setDefender(SceneObject defender); /** * Adds a SceneObject to the defender vector * @pre { this object is locked } * @post { this object is locked, defender is in the defender vector } * @param defender SceneObject to add to the defender vector */ public native void addDefender(SceneObject defender); /** * Removes the specified defender from the defender vector * @pre { this object is locked } * @post { this object is locked, defender is not in the defender vector } * @param defender SceneObject to remove from the defender vector */ public native void removeDefender(SceneObject defender); public native void setDespawnOnNoPlayerInRange(boolean val); /** * Gets called when the creature was despawned */ public abstract native void notifyDespawn(Zone zone); public abstract void scheduleDespawn() { //Despawn in 45 seconds. scheduleDespawn(45); } /** * Schedules despawn of the AiAgent. * @param timeToDespawn the time to despawn the AiAgent in seconds. */ public abstract native void scheduleDespawn(int timeToDespawn); /** * Respawns creature to specified zone with home location position */ public native void respawn(Zone zone, int level); @local public void addPatrolPoint(@dereferenced PatrolPoint point) { synchronized (targetMutex) { patrolPoints.add(point); } } public void setHomeLocation(float x, float z, float y, SceneObject cell = null) { homeLocation.setPosition(x, z, y); homeLocation.setCell(cell); homeLocation.setReached(true); } public void setRespawnTimer(float resp) { respawnTimer = resp; } /** * Evaluates if this object can be attacket by the passed creature object * @pre { this object is locked } * @post { this object is locked } * @return returns true if the creature object can attack this */ public boolean isAttackableBy(CreatureObject object) { if (object == this) return false; if (super.pvpStatusBitmask == 0) return false; if (isRetreating()) return false; if (isDead()) return false; if (object.isAiAgent()) return false; unsigned int targetFaction = object.getFaction(); if (targetFaction != 0 && super.getFaction() != 0) { PlayerObject ghost = object.getPlayerObject(); if (targetFaction == getFaction()) return false; if (ghost != null && (targetFaction != super.getFaction()) && ghost.getFactionStatus() == FactionStatus.ONLEAVE) return false; } else if (targetFaction == 0 && super.getFaction() != 0) return false; return true; } /** * Evaluates if this creature is aggresive to the object * @pre { } * @post { } * @return returns true if its aggressive */ public native boolean isAggressiveTo(CreatureObject object); public void setOblivious() { followState = OBLIVIOUS; setTargetObject(null); activateMovementEvent(); } public void setWatchObject(SceneObject obj) { synchronized (targetMutex) { if (this.isRetreating()) return; followState = WATCHING; setTargetObject(obj); activateMovementEvent(); } } public void setStalkObject(SceneObject obj) { synchronized (targetMutex) { if (this.isRetreating()) return; followState = STALKING; setTargetObject(obj); activateMovementEvent(); } } public void setFollowObject(SceneObject obj) { synchronized (targetMutex) { if (this.isRetreating()) return; followState = FOLLOWING; setTargetObject(obj); activateMovementEvent(); } } public void setTargetObject(SceneObject obj) { synchronized (targetMutex) { if (followObject != obj) { clearPatrolPoints(); followObject = obj; } } } public SceneObject getFollowObject() { return followObject; } public native void selectWeapon(); public native boolean validateStateAttack(CreatureObject target, string args); public boolean isRetreating() { return !homeLocation.isReached(); } public boolean isFleeing() { return fleeing; } public native void clearDespawnEvent(); public float getKinetic() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getKinetic(); } public float getEnergy() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getEnergy(); } public float getElectricity() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getElectricity(); } public float getStun() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getStun(); } public float getBlast() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getBlast(); } public float getHeat() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getHeat(); } public float getCold() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getCold(); } public float getAcid() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getAcid(); } public float getLightSaber() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getLightSaber(); } public boolean isStalker() { if (npcTemplate.get() == null) return false; return npcTemplate.get().isStalker(); } public boolean isKiller() { if (npcTemplate.get() == null) return false; return npcTemplate.get().isKiller(); } public unsigned int getFerocity() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getFerocity(); } public unsigned int getArmor() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getArmor(); } public boolean getDespawnOnNoPlayerInRange() { return despawnOnNoPlayerInRange; } public int getNumberOfPlayersInRange() { return numberOfPlayersInRange; } public string getFactionString() { if (npcTemplate.get() == null) return ""; return npcTemplate.get().getFaction(); } public string getSocialGroup() { if (npcTemplate.get() == null) return ""; return npcTemplate.get().getSocialGroup(); } public float getChanceHit() { if (npcTemplate.get() == null) return false; return npcTemplate.get().getChanceHit(); } public int getDamageMin() { if (npcTemplate.get() == null) return 0; if (getWeapon() == null) return 0; return getWeapon().getMinDamage(); //return npcTemplate.get().getDamageMin(); //return calculateAttackMinDamage(super.getLevel()); } public int getDamageMax() { if (npcTemplate.get() == null) return 0; if (getWeapon() == null) return 0; //return npcTemplate.get().getDamageMax(); return getWeapon().getMaxDamage(); } public int getBaseXp() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getBaseXp(); } public unsigned int getDiet() { if (npcTemplate.get() == null) return 0; return npcTemplate.get().getDiet(); } @local public CreatureAttackMap getAttackMap() { if (npcTemplate.get() == null) return null; return npcTemplate.get().getAttacks(); } @local public LootGroupCollection getLootGroups() { if (npcTemplate.get() == null) return null; return npcTemplate.get().getLootGroups(); } public float getRespawnTimer() { return respawnTimer; } @local public PatrolPoint getHomeLocation() { return homeLocation; } public boolean isAiAgent() { return true; } @local public CreatureTemplate getCreatureTemplate() { return npcTemplate.get(); } public native boolean hasLoot(); public void setShowNextPosition(boolean val) { showNextMovementPosition = val; } }