mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-15 00:08:07 -04:00
3031 lines
99 KiB
C++
Executable File
3031 lines
99 KiB
C++
Executable File
// ======================================================================
|
|
//
|
|
// AiCreatureController.cpp
|
|
//
|
|
// copyright 2003, sony online entertainment
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "serverGame/FirstServerGame.h"
|
|
#include "serverGame/AiCreatureController.h"
|
|
|
|
#include "serverGame/AiMovementBase.h"
|
|
#include "serverGame/AiMovementSwarm.h"
|
|
#include "serverGame/AiMovementFace.h"
|
|
#include "serverGame/AiMovementFlee.h"
|
|
#include "serverGame/AiMovementFollow.h"
|
|
#include "serverGame/AiMovementIdle.h"
|
|
#include "serverGame/AiMovementLoiter.h"
|
|
#include "serverGame/AiMovementMove.h"
|
|
#include "serverGame/AiMovementPathFollow.h"
|
|
#include "serverGame/AiMovementPatrol.h"
|
|
#include "serverGame/AiMovementWander.h"
|
|
#include "serverGame/AiMovementWanderInterior.h"
|
|
#include "serverGame/AiCreatureCombatProfile.h"
|
|
#include "serverGame/AiCreatureData.h"
|
|
#include "serverGame/AiLogManager.h"
|
|
#include "serverGame/CellObject.h"
|
|
#include "serverGame/ConfigServerGame.h"
|
|
#include "serverGame/ContainerInterface.h"
|
|
#include "serverGame/CreatureObject.h"
|
|
#include "serverGame/GameServer.h"
|
|
#include "serverGame/NameManager.h"
|
|
#include "serverGame/ObjectTracker.h"
|
|
#include "serverGame/ServerMessageForwarding.h"
|
|
#include "serverGame/ServerWorld.h"
|
|
#include "serverGame/WeaponObject.h"
|
|
#include "serverNetworkMessages/AiMovementMessage.h"
|
|
#include "serverNetworkMessages/AiCreatureStateMessage.h"
|
|
#include "serverPathfinding/CityPathGraphManager.h"
|
|
#include "serverScript/GameScriptObject.h"
|
|
#include "serverScript/ScriptParameters.h"
|
|
#include "serverScript/ScriptFunctionTable.h"
|
|
#include "sharedCollision/CollisionProperty.h"
|
|
#include "sharedCollision/CollisionUtils.h"
|
|
#include "sharedCollision/CollisionWorld.h"
|
|
#include "sharedCollision/Floor.h"
|
|
#include "sharedCollision/FloorLocator.h"
|
|
#include "sharedCollision/Footprint.h"
|
|
#include "sharedDebug/PerformanceTimer.h"
|
|
#include "sharedDebug/Profiler.h"
|
|
#include "sharedFile/FileNameUtils.h"
|
|
#include "sharedFoundation/ConstCharCrcString.h"
|
|
#include "sharedFoundation/FormattedString.h"
|
|
#include "sharedFoundation/Os.h"
|
|
#include "sharedGame/AiDebugString.h"
|
|
#include "sharedGame/SharedCreatureObjectTemplate.h"
|
|
#include "sharedUtility/Location.h"
|
|
#include "sharedLog/Log.h"
|
|
#include "sharedNetworkMessages/AIDebuggingMessages.h"
|
|
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
|
|
#include "sharedNetworkMessages/MessageQueueGenericValueType.h"
|
|
#include "sharedObject/AlterResult.h"
|
|
#include "sharedObject/Appearance.h"
|
|
#include "sharedObject/AppearanceTemplate.h"
|
|
#include "sharedObject/CellProperty.h"
|
|
#include "sharedObject/ObjectTemplateList.h"
|
|
#include "sharedObject/NetworkIdManager.h"
|
|
#include "sharedUtility/Location.h"
|
|
#include "UnicodeUtils.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using namespace Scripting;
|
|
|
|
// ======================================================================
|
|
//
|
|
// AiCreatureControllerNamespace
|
|
//
|
|
// ======================================================================
|
|
|
|
namespace AiCreatureControllerNamespace
|
|
{
|
|
bool s_installed = false;
|
|
typedef std::set<Client *> ObserverList;
|
|
|
|
PersistentCrcString * s_defaultCreatureName = NULL;
|
|
|
|
void remove();
|
|
Location getLocation(ServerObject const & serverObject);
|
|
}
|
|
|
|
using namespace AiCreatureControllerNamespace;
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AiCreatureControllerNamespace::remove()
|
|
{
|
|
DEBUG_FATAL(!s_installed, ("Not installed."));
|
|
|
|
delete s_defaultCreatureName;
|
|
s_defaultCreatureName = NULL;
|
|
|
|
s_installed = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
Location AiCreatureControllerNamespace::getLocation(ServerObject const & serverObject)
|
|
{
|
|
CellProperty const * const cellProperty = serverObject.getParentCell();
|
|
Object const * const cellObject = (cellProperty != NULL) ? &cellProperty->getOwner() : NULL;
|
|
Vector const & positionRelativeToCellOrWorld = (cellObject != NULL) ? serverObject.getPosition_c() : serverObject.getPosition_w();
|
|
NetworkId const & networkIdForCellOrWorld = (cellObject != NULL) ? cellObject->getNetworkId() : NetworkId::cms_invalid;
|
|
|
|
return Location(positionRelativeToCellOrWorld, networkIdForCellOrWorld, Location::getCrcBySceneName(serverObject.getSceneId()));
|
|
}
|
|
|
|
// ======================================================================
|
|
//
|
|
// AiCreatureController
|
|
//
|
|
// ======================================================================
|
|
|
|
//-----------------------------------------------------------------------
|
|
AICreatureController::AICreatureController(CreatureObject * newOwner)
|
|
: CreatureController(newOwner)
|
|
, m_movement()
|
|
, m_pendingMovement()
|
|
, m_suspendedMovement()
|
|
, m_lastStartPosition(newOwner->getPosition_p())
|
|
, m_lastEndPosition(newOwner->getPosition_p())
|
|
, m_sleepTimer(0.0f)
|
|
, m_inPathfindingRegion(false)
|
|
, m_stuckCounter(0)
|
|
, m_authority(false)
|
|
, m_speed(0.0f)
|
|
, m_aiCreatureData(&AiCreatureData::getDefaultCreatureData())
|
|
, m_primaryWeaponActions()
|
|
, m_secondaryWeaponActions()
|
|
, m_running(false)
|
|
, m_homeLocation()
|
|
, m_primaryWeapon()
|
|
, m_secondaryWeapon()
|
|
, m_frozen(false)
|
|
, m_combatStartLocation()
|
|
, m_retreating(false)
|
|
, m_retreatingStartTime(0)
|
|
, m_logging(false)
|
|
, m_creatureName()
|
|
, m_hibernationDelay(0)
|
|
, m_hibernationTimer(0)
|
|
#ifdef _DEBUG
|
|
, m_aiDebugStringCrc(0)
|
|
#endif // _DEBUG
|
|
, m_invalidTarget(false)
|
|
{
|
|
m_creatureName.setSourceObject(this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
AICreatureController::~AICreatureController()
|
|
{
|
|
m_movement = AiMovementBaseNullPtr;
|
|
m_pendingMovement = AiMovementBaseNullPtr;
|
|
m_suspendedMovement = AiMovementBaseNullPtr;
|
|
|
|
Object * const owner = getOwner();
|
|
|
|
if (owner != NULL && owner->isAuthoritative() && !getHibernate() && m_hibernationDelay.get() > 0 && m_hibernationTimer.get() > 0)
|
|
ObjectTracker::removeDelayedHibernatingAI();
|
|
|
|
if ( (owner != NULL)
|
|
&& AiLogManager::isLogging(owner->getNetworkId()))
|
|
{
|
|
AiLogManager::setLogging(owner->getNetworkId(), false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::install()
|
|
{
|
|
DEBUG_FATAL(s_installed, ("Already installed."));
|
|
|
|
LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", (""));
|
|
LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", ("-------- AI DEBUG - NEW SESSION --------"));
|
|
LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", (""));
|
|
|
|
AiCreatureCombatProfile::install();
|
|
AiCreatureData::install();
|
|
|
|
s_defaultCreatureName = new PersistentCrcString("NonCombatAI", false);
|
|
|
|
ExitChain::add(AiCreatureControllerNamespace::remove, "AiCreatureControllerNamespace::remove");
|
|
s_installed = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::CreatureNameChangedCallback::modified(AICreatureController & target, std::string const & /*oldValue*/, std::string const & newValue, bool /*local*/) const
|
|
{
|
|
target.onCreatureNameChanged(newValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::handleMessage (int message, float value, const MessageQueue::Data* const data, uint32 flags)
|
|
{
|
|
CreatureObject * const owner = static_cast<CreatureObject*>(getOwner());
|
|
if (owner == NULL)
|
|
{
|
|
DEBUG_FATAL(true, ("Owner is NULL in AiCreatureController::handleMessage\n"));
|
|
return;
|
|
}
|
|
|
|
switch (message)
|
|
{
|
|
case CM_aiSetMovement:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetMovement) owner(%s)", getDebugInformation().c_str()));
|
|
|
|
AiMovementMessage const * const msg = safe_cast<const AiMovementMessage *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
if (owner->isAuthoritative())
|
|
{
|
|
WARNING(true, ("AICreatureController::handleMessage() owner(%s) Received CM_aiSetMovement(%d) for an authoritative object. Only proxied objects should receive this controller message.", getDebugInformation().c_str(), static_cast<int>(msg->getMovementType())));
|
|
}
|
|
else
|
|
{
|
|
if (msg->getMovement() != AiMovementBaseNullPtr)
|
|
{
|
|
changeMovement(msg->getMovement());
|
|
}
|
|
else
|
|
{
|
|
changeMovement(AiMovementBaseNullPtr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiSetMovementRun:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetMovementRun) owner(%s)", getDebugInformation().c_str()));
|
|
setMovementRun();
|
|
}
|
|
break;
|
|
case CM_aiSetMovementWalk:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetMovementWalk) owner(%s)", getDebugInformation().c_str()));
|
|
setMovementWalk();
|
|
}
|
|
break;
|
|
case CM_aiSetCreatureName:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetCreatureName) owner(%s)", getDebugInformation().c_str()));
|
|
MessageQueueGenericValueType<std::string> const * const msg = safe_cast<MessageQueueGenericValueType<std::string> const *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
setCreatureName(msg->getValue());
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiSetHomeLocation:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetHomeLocation) owner(%s)", getDebugInformation().c_str()));
|
|
MessageQueueGenericValueType<Location> const * const msg = safe_cast<MessageQueueGenericValueType<Location> const *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
setHomeLocation(msg->getValue());
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiSetFrozen:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetFrozen) owner(%s)", getDebugInformation().c_str()));
|
|
MessageQueueGenericValueType<bool> const * const msg = safe_cast<MessageQueueGenericValueType<bool> const *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
setFrozen(msg->getValue());
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiSetRetreating:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetRetreating) owner(%s)", getDebugInformation().c_str()));
|
|
MessageQueueGenericValueType<bool> const * const msg = safe_cast<MessageQueueGenericValueType<bool> const *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
setRetreating(msg->getValue());
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiSetLogging:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiSetLogging) owner(%s)", getDebugInformation().c_str()));
|
|
MessageQueueGenericValueType<bool> const * const msg = safe_cast<MessageQueueGenericValueType<bool> const *>(data);
|
|
|
|
if (msg != NULL)
|
|
{
|
|
setLogging(msg->getValue());
|
|
}
|
|
}
|
|
break;
|
|
case CM_aiMarkCombatStartLocation:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiMarkCombatStartLocation) owner(%s)", getDebugInformation().c_str()));
|
|
markCombatStartLocation();
|
|
}
|
|
break;
|
|
case CM_aiEquipPrimaryWeapon:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiEquipPrimaryWeapon) owner(%s)", getDebugInformation().c_str()));
|
|
equipPrimaryWeapon();
|
|
}
|
|
break;
|
|
case CM_aiEquipSecondaryWeapon:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiEquipSecondaryWeapon) owner(%s)", getDebugInformation().c_str()));
|
|
equipSecondaryWeapon();
|
|
}
|
|
break;
|
|
case CM_aiUnEquipWeapons:
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::handleMessage(CM_aiUnEquipWeapons) owner(%s)", getDebugInformation().c_str()));
|
|
unEquipWeapons();
|
|
}
|
|
break;
|
|
case CM_setHibernationDelay:
|
|
{
|
|
MessageQueueGenericValueType<float> const * const msg = safe_cast<MessageQueueGenericValueType<float> const *>(data);
|
|
if (msg != NULL)
|
|
setHibernationDelay(msg->getValue());
|
|
}
|
|
break;
|
|
default:
|
|
CreatureController::handleMessage(message, value, data, flags);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::setSpeed(float const speed)
|
|
{
|
|
m_speed = speed;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
float AICreatureController::getSpeed() const
|
|
{
|
|
return m_speed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::isRunning ( void ) const
|
|
{
|
|
float runSpeed = getCreature()->getRunSpeed();
|
|
float walkSpeed = getCreature()->getWalkSpeed();
|
|
|
|
// This is a hack to make swimming always go at a constant speed.
|
|
|
|
if (m_running.get())
|
|
{
|
|
return (runSpeed != 0);
|
|
}
|
|
else
|
|
{
|
|
return (walkSpeed == 0);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getAcceleration ( void ) const
|
|
{
|
|
float base;
|
|
|
|
if (isRunning())
|
|
{
|
|
base = getCreature()->getAcceleration(SharedCreatureObjectTemplate::MT_run);
|
|
}
|
|
else
|
|
{
|
|
base = getCreature()->getAcceleration(SharedCreatureObjectTemplate::MT_walk);
|
|
}
|
|
|
|
float scale = getCreature()->getAccelScale();
|
|
float modifier = getCreature()->getAccelPercent();
|
|
|
|
return base * scale * modifier;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::moveTowards( CellProperty const * cell, Vector const & position, float time )
|
|
{
|
|
turnToward(cell, position);
|
|
|
|
float const stepDistance = getSpeed() * time;
|
|
IGNORE_RETURN(moveAlongGround( cell, position, stepDistance ));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getDistanceToLocationSquared(CellProperty const * cell, Vector const & point) const
|
|
{
|
|
Vector localPoint = CollisionUtils::transformToCell( cell, point, getCreatureCell() );
|
|
Vector creaturePos = getCreaturePosition_p();
|
|
|
|
return creaturePos.magnitudeBetweenSquared(localPoint);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::reachedPoint ( CellProperty const * cell, Vector const & point, float radius ) const
|
|
{
|
|
float const distanceSquared = getDistanceToLocationSquared(cell, point);
|
|
|
|
// We are giving a little tolerance here for floating point error
|
|
|
|
return (distanceSquared <= (std::max(0.0f, sqr(radius)) + 0.01f));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::updateMovementType ( void )
|
|
{
|
|
CreatureObject * const creature = getCreature();
|
|
|
|
Vector newHeading = creature->getPosition_w() - m_lastStartPosition;
|
|
|
|
float speed = getSpeed();
|
|
|
|
if (newHeading.normalize() && (speed > 0))
|
|
{
|
|
setCurrentVelocity(newHeading * speed);
|
|
|
|
if (creature->isAuthoritative())
|
|
{
|
|
if (isRunning())
|
|
creature->setMovementRun();
|
|
else
|
|
creature->setMovementWalk();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setCurrentVelocity(Vector::zero);
|
|
if (creature->isAuthoritative())
|
|
creature->setMovementStationary();
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::realAlter(float time)
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter");
|
|
|
|
//DEBUG_REPORT_LOG(true, ("%u\n", Os::getProcessId()));
|
|
|
|
float alterResult = CreatureController::realAlter(time);
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
#ifdef _DEBUG
|
|
AiDebugString * aiDebugString = NULL;
|
|
if (!creatureOwner->getObservers().empty())
|
|
{
|
|
aiDebugString = new AiDebugString;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// tick down our hibernation timer
|
|
if (m_hibernationTimer.get() > 0 && creatureOwner->isAuthoritative())
|
|
{
|
|
if (shouldHibernate())
|
|
{
|
|
// DEBUG_REPORT_LOG(true, ("[aitest] Hibernation timer countdown, timer = %f, time = %f\n", m_hibernationTimer.get(), time));
|
|
if (m_hibernationTimer.get() <= m_hibernationDelay.get())
|
|
m_hibernationTimer = std::max(m_hibernationTimer.get() - time, 0.0f);
|
|
else
|
|
{
|
|
// we want to skip the first frame, due to coming out of hibernation
|
|
m_hibernationTimer = m_hibernationDelay.get();
|
|
}
|
|
if (m_hibernationTimer.get() == 0)
|
|
{
|
|
if (!getHibernate())
|
|
ObjectTracker::removeDelayedHibernatingAI();
|
|
updateHibernate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we are counting down, but we don't need to hibernate anyway; stop the count
|
|
m_hibernationTimer = -1.0f;
|
|
ObjectTracker::removeDelayedHibernatingAI();
|
|
}
|
|
}
|
|
|
|
// creatures not in the world and hibernating creatures should go no further
|
|
if (!creatureOwner->isInWorld() || getHibernate())
|
|
{
|
|
#ifdef _DEBUG
|
|
if (aiDebugString != NULL)
|
|
{
|
|
sendDebugAiToClients(*aiDebugString);
|
|
delete aiDebugString;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// DEBUG_REPORT_LOG(true, ("[aitest] Hibernating %s from alter\n", creatureOwner->getNetworkId().getValueString().c_str()));
|
|
return alterResult;
|
|
}
|
|
|
|
// check floating
|
|
{
|
|
CollisionProperty const * const collision = creatureOwner->getCollisionProperty();
|
|
Footprint const * const foot = (collision != NULL) ? collision->getFootprint() : NULL;
|
|
bool floating = (foot != NULL) ? foot->isFloating() : false;
|
|
|
|
if (floating)
|
|
{
|
|
return alterResult;
|
|
}
|
|
}
|
|
|
|
// check sleeping
|
|
if (m_sleepTimer > 0.0f)
|
|
{
|
|
m_sleepTimer -= time;
|
|
// DEBUG_REPORT_LOG(true, ("[aitest] Sleeping %s for %f from alter\n", creatureOwner->getNetworkId().getValueString().c_str(), m_sleepTimer));
|
|
AlterResult::incorporateLongAlterResult(alterResult, m_sleepTimer);
|
|
return alterResult;
|
|
}
|
|
|
|
// change behaviors
|
|
if (getPendingMovementType() != AMT_invalid)
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter: getPendingMovementType() != AMT_invalid");
|
|
|
|
// Update our inPathfindingRegion flag whenever the behavior changes
|
|
CityPathGraph const * graph = CityPathGraphManager::getCityGraphFor(creatureOwner);
|
|
m_inPathfindingRegion = (graph != NULL);
|
|
applyMovementChange();
|
|
}
|
|
|
|
// alter behaviors
|
|
Vector newStartPosition = getCreature()->getPosition_w();
|
|
|
|
if ( !creatureOwner->isDead()
|
|
&& creatureOwner->isAuthoritative())
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter: !creatureOwner->isDead() && creatureOwner->isAuthoritative()");
|
|
|
|
// This is to help catch and prevent bugs with retreating until all the kinks are worked out
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter: isRetreating()");
|
|
|
|
int const retreatingTime = static_cast<int>(Os::getRealSystemTime() - m_retreatingStartTime.get());
|
|
int const maxRetreatTime = 80;
|
|
|
|
if (retreatingTime > maxRetreatTime)
|
|
{
|
|
AiLocation location(m_combatStartLocation.get().getCell(), m_combatStartLocation.get().getCoordinates(), 0.0f);
|
|
warpTo(location.getCell(), location.getPosition_p());
|
|
|
|
setRetreating(false);
|
|
|
|
WARNING(true, ("AICreatureController::realAlter() owner(%s) inCombat(%s) Retreating has lasted(%d) > %d seconds. Auto-releasing retreat.", getDebugInformation().c_str(), getCreature()->isInCombat() ? "yes" : "no", retreatingTime, maxRetreatTime));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retreating - See if the hate list timer needs to be reset
|
|
{
|
|
if (!creatureOwner->isHateListEmpty())
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter: !creatureOwner->isHateListEmpty()");
|
|
|
|
bool resetHateTimer = false;
|
|
CachedNetworkId const & hateTarget = creatureOwner->getHateTarget();
|
|
CreatureObject * const hateTargetCreatureObject = CreatureObject::asCreatureObject(hateTarget.getObject());
|
|
CreatureController const * const hateTargetCreatureController = (hateTargetCreatureObject != NULL) ? CreatureController::asCreatureController(hateTargetCreatureObject->getController()) : NULL;
|
|
|
|
if ( (hateTargetCreatureObject != NULL)
|
|
&& (hateTargetCreatureController != NULL))
|
|
{
|
|
float const hateTargetMovementSpeedSquared = hateTargetCreatureController->getCurrentVelocity().magnitudeSquared();
|
|
float const hateTargetWalkSpeedSquared = sqr(hateTargetCreatureObject->getWalkSpeed());
|
|
|
|
if (hateTargetMovementSpeedSquared <= hateTargetWalkSpeedSquared)
|
|
{
|
|
resetHateTimer = true;
|
|
}
|
|
else
|
|
{
|
|
Object * const combatStartLocationCell = NetworkIdManager::getObjectById(m_combatStartLocation.get().getCell());
|
|
Vector const & combatStartPosition_c = m_combatStartLocation.get().getCoordinates();
|
|
Vector const & combatStartPosition_w = (combatStartLocationCell != NULL) ? combatStartLocationCell->rotateTranslate_o2w(combatStartPosition_c) : combatStartPosition_c;
|
|
float const distanceToCombatStartLocationSquared = creatureOwner->getPosition_w().magnitudeBetweenSquared(combatStartPosition_w);
|
|
float const aggroRadius = getAggroRadius();
|
|
|
|
if (distanceToCombatStartLocationSquared <= sqr(aggroRadius * 1.2f))
|
|
{
|
|
resetHateTimer = true;
|
|
}
|
|
}
|
|
|
|
if (resetHateTimer)
|
|
{
|
|
creatureOwner->resetHateTimer();
|
|
hateTargetCreatureObject->resetHateTimer();
|
|
}
|
|
}
|
|
else if (hateTarget.getObject() != NULL)
|
|
{
|
|
// AI don't need to lose interest in stationary AI
|
|
|
|
creatureOwner->resetHateTimer();
|
|
}
|
|
|
|
// Fix up a bad hate list / no available target state.
|
|
if(!hateTarget.isValid())
|
|
{
|
|
if(m_invalidTarget)
|
|
{
|
|
creatureOwner->clearHateList();
|
|
m_invalidTarget = false;
|
|
}
|
|
else
|
|
m_invalidTarget = true; // give the system 1 frame to try and aquire a new valid target.
|
|
}
|
|
else
|
|
m_invalidTarget = false;
|
|
}
|
|
}
|
|
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
PROFILER_AUTO_BLOCK_DEFINE("AiCreatureController::realAlter: m_movement != AiMovementBaseNullPtr");
|
|
|
|
PerformanceTimer timer;
|
|
timer.start();
|
|
|
|
// it's somehow possible that m_movement will get deleted during its
|
|
// alter, so increment its ref count to prevent that
|
|
{
|
|
AiMovementBasePtr temp(m_movement);
|
|
|
|
if (!m_frozen.get())
|
|
{
|
|
m_movement->alter(time);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (aiDebugString != NULL)
|
|
{
|
|
m_movement->addDebug(*aiDebugString);
|
|
}
|
|
#endif // _DEBUG
|
|
temp = AiMovementBaseNullPtr;
|
|
}
|
|
|
|
timer.stop();
|
|
|
|
float msec = timer.getElapsedTime() * 1000.0f;
|
|
|
|
// AI movements should hopefully never take 30 milliseconds
|
|
|
|
if ( (m_movement != AiMovementBaseNullPtr)
|
|
#ifdef _DEBUG
|
|
&& (msec >= 60.0f))
|
|
#else
|
|
&& (msec >= 30.0f))
|
|
#endif // _DEBUG
|
|
{
|
|
std::string warningString;
|
|
m_movement->getDebugInfo(warningString);
|
|
|
|
WARNING(true, (FormattedString<4096>().sprintf("AICreatureController::realAlter() owner(%s) movement(%s) alter took %.0fms. Dumping movement info - %s\n", getDebugInformation().c_str(), AiMovementBase::getMovementString(m_movement->getType()), msec, warningString.c_str())));
|
|
}
|
|
}
|
|
}
|
|
|
|
m_lastStartPosition = newStartPosition;
|
|
m_lastEndPosition = creatureOwner->getPosition_w();
|
|
|
|
updateMovementType();
|
|
|
|
#ifdef _DEBUG
|
|
if (aiDebugString != NULL)
|
|
{
|
|
sendDebugAiToClients(*aiDebugString);
|
|
delete aiDebugString;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// done
|
|
return alterResult;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void AICreatureController::changeMovement(AiMovementBasePtr newMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::changeMovement() movement(%s)", (newMovement != AiMovementBaseNullPtr) ? AiMovementBase::getMovementString(newMovement->getType()) : "NULL"));
|
|
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
if (m_pendingMovement != newMovement)
|
|
{
|
|
m_pendingMovement = newMovement;
|
|
m_sleepTimer = 0.0f;
|
|
|
|
getOwner()->scheduleForAlter();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_movement = newMovement;
|
|
m_pendingMovement = AiMovementBaseNullPtr;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Stops the current movement type of the creature, allowing it to be resumed at a
|
|
* later time.
|
|
*
|
|
* NOTE: Only one movement can be suspended at a time. If this function
|
|
* is called while a previous movement is suspended, it will fail.
|
|
*
|
|
* @return true if the movement has been suspended, false if not.
|
|
*/
|
|
bool AICreatureController::suspendMovement()
|
|
{
|
|
bool result = true;
|
|
if (m_suspendedMovement == AiMovementBaseNullPtr)
|
|
{
|
|
m_suspendedMovement = m_movement;
|
|
stop();
|
|
}
|
|
else if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
result = false;
|
|
WARNING(true, ("AICreatureController::suspendMovement called for creature %s when there is already a suspended movement type %d",
|
|
getOwner()->getNetworkId().getValueString().c_str(), static_cast<int>(m_movement->getType())));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Resumes a previously suspended movement.
|
|
*
|
|
* @return true if the movement was resumed, false if there was no previous movement.
|
|
*/
|
|
bool AICreatureController::resumeMovement()
|
|
{
|
|
bool result = false;
|
|
if (m_suspendedMovement != AiMovementBaseNullPtr)
|
|
{
|
|
result = true;
|
|
m_pendingMovement = m_suspendedMovement;
|
|
m_suspendedMovement = AiMovementBaseNullPtr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::hasSuspendedMovement()
|
|
{
|
|
return m_suspendedMovement != AiMovementBaseNullPtr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::applyMovementChange()
|
|
{
|
|
if (m_movement != m_pendingMovement)
|
|
{
|
|
m_movement = m_pendingMovement;
|
|
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
sendMessageToProxyServer(CM_aiSetMovement, new AiMovementMessage(getOwner()->getNetworkId(), m_movement));
|
|
(safe_cast<ServerObject*>(getOwner()))->setDefaultAlterTime(m_movement->getDefaultAlterTime());
|
|
}
|
|
else
|
|
{
|
|
sendMessageToProxyServer(CM_aiSetMovement, new AiMovementMessage(getOwner()->getNetworkId()));
|
|
(safe_cast<ServerObject*>(getOwner()))->setDefaultAlterTime(AlterResult::cms_keepNoAlter);
|
|
}
|
|
// note: we need to do this now or updateHibernate will fail
|
|
m_pendingMovement = AiMovementBaseNullPtr;
|
|
updateHibernate();
|
|
}
|
|
}
|
|
|
|
m_pendingMovement = AiMovementBaseNullPtr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::hasPendingMovement() const
|
|
{
|
|
return m_pendingMovement != AiMovementBaseNullPtr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::getBehaviorDebugInfo (std::string & outString) const
|
|
{
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
m_movement->getDebugInfo(outString);
|
|
}
|
|
else
|
|
{
|
|
outString += "No Behavior\n";
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|
|
// Wander
|
|
// ======================================================================
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::wander( float minDistance, float maxDistance, float minAngle, float maxAngle, float minDelay, float maxDelay)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::wander() owner(%s) minDistance(%.2f) maxDistance(%.2f) minAngle(%.2f) maxAngle(%.2f) minDelay(%.2f) maxDelay(%.2f) Trying to wander while retreating, failing request.", getDebugInformation().c_str(), minDistance, maxDistance, minAngle, maxAngle, minDelay, maxDelay));
|
|
return false;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::wander() owner(%s) minDistance(%.2f) maxDistance(%.2f) minAngle(%.2f) maxAngle(%.2f) minDelay(%.2f) maxDelay(%.2f)", getDebugInformation().c_str(), minDistance, maxDistance, minAngle, maxAngle, minDelay, maxDelay));
|
|
|
|
if((minDistance == 0) && (maxDistance == 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (getCreature()->getParentCell() != CellProperty::getWorldCellProperty())
|
|
{
|
|
AiMovementBasePtr movement(new AiMovementWanderInterior(this));
|
|
changeMovement(movement);
|
|
}
|
|
else
|
|
{
|
|
AiMovementBasePtr movement(new AiMovementWander(this, minDistance, maxDistance, minAngle, maxAngle, minDelay, maxDelay));
|
|
changeMovement(movement);
|
|
}
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
// ======================================================================
|
|
// Loiter
|
|
// ======================================================================
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::loiter(CellProperty const * homeCell, Vector const & home_p, float const minDistance, float const maxDistance, float const minDelay, float const maxDelay)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::loiter() owner(%s) cell(%s) home(%.0f, %.0f, %.0f) distance[%.2f...%.2f] delay[%.0f...%.0f] Trying to loiter while retreating, failing request.", getDebugInformation().c_str(), ((homeCell != NULL) ? homeCell->getCellName() : "NULL"), home_p.x, home_p.y, home_p.z, minDistance, maxDistance, minDelay, maxDelay));
|
|
return;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::loiter() owner(%s) cell(%s) home(%.0f, %.0f, %.0f) distance[%.2f...%.2f] delay[%.0f...%.0f]", getDebugInformation().c_str(), ((homeCell != NULL) ? homeCell->getCellName() : "NULL"), home_p.x, home_p.y, home_p.z, minDistance, maxDistance, minDelay, maxDelay));
|
|
|
|
AiMovementBasePtr movement(new AiMovementLoiter(this, homeCell, home_p, minDistance, maxDistance, minDelay, maxDelay));
|
|
|
|
changeMovement(movement);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::loiter(NetworkId const & homeId, float const minDistance, float const maxDistance, float const minDelay, float const maxDelay)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::loiter() owner(%s) home(%s) distance[%.2f...%.2f] delay[%.0f...%.0f] Trying to loiter while retreating, failing request.", getDebugInformation().c_str(), homeId.getValueString().c_str(), minDistance, maxDistance, minDelay, maxDelay));
|
|
return;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::loiter() owner(%s) home(%s) distance[%.2f...%.2f] delay[%.0f...%.0f]", getDebugInformation().c_str(), homeId.getValueString().c_str(), minDistance, maxDistance, minDelay, maxDelay));
|
|
|
|
ServerObject const * const home = ServerWorld::findObjectByNetworkId(homeId);
|
|
AiMovementBasePtr movement(new AiMovementLoiter(this,home,minDistance,maxDistance,minDelay,maxDelay));
|
|
|
|
changeMovement(movement);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::moveTo(CellProperty const * cell, Vector const & target_p, float const radius)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::moveTo() owner(%s) cell(%s) position(%.0f, %.0f, %.0f) radius(%.2f) Trying to moveTo while retreating, failing request.", getDebugInformation().c_str(), ((cell != NULL) ? cell->getCellName() : "NULL"), target_p.x, target_p.y, target_p.z, radius));
|
|
return;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_move)
|
|
{
|
|
AiMovementMove * const aiMovementMove = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementMove() : NULL;
|
|
|
|
if (aiMovementMove != NULL)
|
|
{
|
|
AiLocation const & target = aiMovementMove->getTarget();
|
|
|
|
if ( (target.getCell() == cell)
|
|
&& (target.getPosition_p() == target_p)
|
|
&& (target.getRadius() == radius))
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::moveTo() owner(%s) cell(%s) position(%.0f, %.0f, %.0f) radius(%.2f)", getDebugInformation().c_str(), ((cell != NULL) ? cell->getCellName() : "NULL"), target_p.x, target_p.y, target_p.z, radius));
|
|
|
|
AiMovementBasePtr movement(new AiMovementMove(this, cell, target_p, radius));
|
|
|
|
changeMovement(movement);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::moveTo(Unicode::String const & targetName)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::moveTo() owner(%s) target(%s) Trying to moveTo while retreating, failing request.", getDebugInformation().c_str(), Unicode::wideToNarrow(targetName).c_str()));
|
|
return;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_move)
|
|
{
|
|
AiMovementMove * const aiMovementMove = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementMove() : NULL;
|
|
|
|
if (aiMovementMove != NULL)
|
|
{
|
|
if (aiMovementMove->getTargetName() == targetName)
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::moveTo() owner(%s) targetName(%s)", getDebugInformation().c_str(), Unicode::wideToNarrow(targetName).c_str()));
|
|
|
|
AiMovementBasePtr behavior(new AiMovementMove(this, targetName));
|
|
|
|
changeMovement(behavior);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::patrol( std::vector<Location> const & locations, bool random, bool flip, bool repeat, int startPoint)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::patrol() owner(%s) Trying to patrol while retreating, failing request.", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_patrol)
|
|
{
|
|
AiMovementPatrol * const aiMovementPatrol = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementPatrol() : NULL;
|
|
if (aiMovementPatrol != NULL)
|
|
{
|
|
// if ()
|
|
// {
|
|
// duplicateMovement = true;
|
|
// }
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::patrol() owner(%s)", getDebugInformation().c_str()));
|
|
|
|
AiMovementBasePtr movement(new AiMovementPatrol(this, locations, random, flip, repeat, startPoint));
|
|
|
|
changeMovement(movement);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::patrol( std::vector<Unicode::String> const & locations, bool random, bool flip, bool repeat, int startPoint)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::patrol() owner(%s) Trying to patrol while retreating, failing request.", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_patrol)
|
|
{
|
|
AiMovementPatrol * const aiMovementPatrol = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementPatrol() : NULL;
|
|
if (aiMovementPatrol != NULL)
|
|
{
|
|
// if ()
|
|
// {
|
|
// duplicateMovement = true;
|
|
// }
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::patrol() owner(%s)", getDebugInformation().c_str()));
|
|
|
|
AiMovementBasePtr movement(new AiMovementPatrol(this, locations, random, flip, repeat, startPoint));
|
|
|
|
changeMovement(movement);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void AICreatureController::stop()
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::stop() owner(%s) Trying to stop while retreating, failing request.", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
if (getMovementType() != AMT_idle)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::stop() owner(%s)", getDebugInformation().c_str()));
|
|
|
|
AiMovementBasePtr behavior(new AiMovementIdle(this));
|
|
|
|
changeMovement(behavior);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::faceTo(CellProperty const * targetCell, Vector const & target_p)
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::faceTo() owner(%s) cell(%s) position(%.0f, %.0f, %.0f) Trying to faceTo while retreating, failing faceTo request.", getDebugInformation().c_str(), ((targetCell != NULL) ? targetCell->getCellName() : "NULL"), target_p.x, target_p.y, target_p.z));
|
|
return false;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_face)
|
|
{
|
|
AiMovementFace * const aiMovementFace = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementFace() : NULL;
|
|
|
|
if (aiMovementFace != NULL)
|
|
{
|
|
AiLocation const & target = aiMovementFace->getTarget();
|
|
|
|
if ( (target.getCell() == targetCell)
|
|
&& (target.getPosition_p() == target_p))
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::faceTo() owner(%s) cell(%s) position(%.0f, %.0f, %.0f)", getDebugInformation().c_str(), ((targetCell != NULL) ? targetCell->getCellName() : "NULL"), target_p.x, target_p.y, target_p.z));
|
|
|
|
AiMovementBasePtr behavior(new AiMovementFace(this, targetCell, target_p));
|
|
|
|
changeMovement(behavior);
|
|
}
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::faceTo( NetworkId const & targetId )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::faceTo() owner(%s) Trying to faceTo the target(%s) while retreating, failing request.", getDebugInformation().c_str(), targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if (!target)
|
|
{
|
|
WARNING(true, ("AICreatureController::faceTo() owner(%s) Unable to resolve target(%s) to a ServerObject", getDebugInformation().c_str(), targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_face)
|
|
{
|
|
AiMovementFace * const aiMovementFace = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementFace() : NULL;
|
|
|
|
if (aiMovementFace != NULL)
|
|
{
|
|
AiLocation const & target = aiMovementFace->getTarget();
|
|
|
|
if (target.getObjectId() == targetId)
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::faceTo() owner(%s) target(%s)", getDebugInformation().c_str(), targetId.getValueString().c_str()));
|
|
|
|
AiMovementBasePtr behavior(new AiMovementFace(this, target));
|
|
changeMovement(behavior);
|
|
}
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool AICreatureController::follow( NetworkId const & targetId, float minDistance, float maxDistance )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::follow() owner(%s) distance[%.2f...%.2f] Trying to follow the target(%s) while retreating, failing request.", getDebugInformation().c_str(), minDistance, maxDistance, targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if (!target)
|
|
{
|
|
WARNING(true, ("AICreatureController::follow() owner(%s) distance[%.2f...%.2f] Unable to resolve target(%s) to a ServerObject", getDebugInformation().c_str(), minDistance, maxDistance, targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_follow)
|
|
{
|
|
AiMovementFollow * const aiMovementFollow = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementFollow() : NULL;
|
|
|
|
if (aiMovementFollow != NULL)
|
|
{
|
|
AiLocation const & target = aiMovementFollow->getTarget();
|
|
|
|
if ( (targetId == target.getObjectId())
|
|
&& (WithinEpsilonInclusive(minDistance, aiMovementFollow->getMinDistance(), 0.001f))
|
|
&& (WithinEpsilonInclusive(maxDistance, aiMovementFollow->getMaxDistance(), 0.001f)))
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::follow() owner(%s) target(%s) distance[%.2f...%.2f]", getDebugInformation().c_str(), targetId.getValueString().c_str(), minDistance, maxDistance));
|
|
|
|
AiMovementBasePtr behavior(new AiMovementFollow(this, target, minDistance, maxDistance));
|
|
|
|
changeMovement(behavior);
|
|
}
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
bool AICreatureController::follow( NetworkId const & targetId, Vector const & offset )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::follow() owner(%s) offset(%.0f, %.0f, %.0f) Trying to follow the target(%s) while retreating, failing request.", getDebugInformation().c_str(), offset.x, offset.y, offset.z, targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if (!target)
|
|
{
|
|
WARNING(true, ("AICreatureController::follow() owner(%s) offset(%.0f, %.0f, %.0f) Unable to resolve target(%s) to a ServerObject", getDebugInformation().c_str(), offset.x, offset.y, offset.z, targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
bool duplicateMovement = false;
|
|
|
|
if (getMovementType() == AMT_follow)
|
|
{
|
|
AiMovementFollow * const aiMovementFollow = (m_movement != AiMovementBaseNullPtr) ? m_movement->asAiMovementFollow() : NULL;
|
|
|
|
if (aiMovementFollow != NULL)
|
|
{
|
|
AiLocation const & offsetTarget = aiMovementFollow->getOffsetTarget();
|
|
|
|
if ( (targetId == offsetTarget.getObjectId())
|
|
&& (offset == offsetTarget.getOffset()))
|
|
{
|
|
duplicateMovement = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!duplicateMovement)
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::follow() owner(%s) target(%s) offset(%.0f, %.0f, %.0f)", getDebugInformation().c_str(), targetId.getValueString().c_str(), offset.x, offset.y, offset.z));
|
|
|
|
bool relativeOffset = true;
|
|
float minDistance = 0.0f;
|
|
float maxDistance = 0.0f;
|
|
|
|
AiMovementBasePtr behavior(new AiMovementFollow(this, target, offset, relativeOffset, minDistance, maxDistance));
|
|
|
|
changeMovement(behavior);
|
|
}
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
bool AICreatureController::swarm( NetworkId const & targetId )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::swarm() owner(%s) Trying to flee the target(%s) while retreating, failing request.", getDebugInformation().c_str(), targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::swarm() owner(%s) target(%s)", getDebugInformation().c_str(), targetId.getValueString().c_str()));
|
|
|
|
if(!targetId) return false;
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if(!target) return false;
|
|
|
|
// ----------
|
|
// Check if we're already swarming the specified object.
|
|
if (getMovementType() == AMT_swarm)
|
|
{
|
|
AiMovementSwarm *swarmBehavior = safe_cast<AiMovementSwarm*>(m_movement.get());
|
|
if (swarmBehavior && (swarmBehavior->getTarget().getObjectId() == targetId))
|
|
{
|
|
// We're already swarming this object, keep swarming it with the same behavior.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
AiMovementBasePtr behavior(new AiMovementSwarm(this,target));
|
|
|
|
changeMovement(behavior);
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
bool AICreatureController::swarm( NetworkId const & targetId, float offset )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::swarm() owner(%s) offset(%.2f) Trying to flee the target(%s) while retreating, failing request.", getDebugInformation().c_str(), offset, targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::swarm() owner(%s) target(%s) offset(%.2f)", getDebugInformation().c_str(), targetId.getValueString().c_str(), offset));
|
|
|
|
if(!targetId) return false;
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if(!target) return false;
|
|
|
|
AiMovementBasePtr behavior(new AiMovementSwarm(this, target, offset));
|
|
|
|
changeMovement(behavior);
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
bool AICreatureController::flee( NetworkId const & targetId, float minDistance, float maxDistance )
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
WARNING(true, ("AICreatureController::flee() owner(%s) distance[%.2f...%.2f] Trying to flee the target(%s) while retreating, failing request.", getDebugInformation().c_str(), minDistance, maxDistance, targetId.getValueString().c_str(), targetId.getValueString().c_str()));
|
|
return false;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::flee() owner(%s) target(%s) distance[%.2f...%.2f]", getDebugInformation().c_str(), targetId.getValueString().c_str(), minDistance, maxDistance));
|
|
|
|
if(getCreatureCell() != CellProperty::getWorldCellProperty())
|
|
{
|
|
AiMovementBasePtr behavior(new AiMovementIdle(this));
|
|
|
|
changeMovement(behavior);
|
|
|
|
return false;
|
|
}
|
|
|
|
if(!targetId) return false;
|
|
|
|
ServerObject const * target = ServerWorld::findObjectByNetworkId(targetId);
|
|
|
|
if(!target) return false;
|
|
|
|
// ----------
|
|
|
|
AiMovementBasePtr behavior(new AiMovementFlee(this, target, minDistance, maxDistance));
|
|
|
|
changeMovement(behavior);
|
|
|
|
return true; //lint !e429 // custodial pointer 'behavior' has not been freed or returned // okay: ownership transfers via changeMovement().
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
CreatureObject const * AICreatureController::getCreature ( void ) const
|
|
{
|
|
return safe_cast< CreatureObject const * >( getOwner() );
|
|
}
|
|
|
|
CreatureObject * AICreatureController::getCreature ( void )
|
|
{
|
|
return safe_cast< CreatureObject * >( getOwner() );
|
|
}
|
|
|
|
CellProperty const * AICreatureController::getCreatureCell ( void ) const
|
|
{
|
|
return getOwner()->getParentCell();
|
|
}
|
|
|
|
Vector AICreatureController::getCreaturePosition_p ( void ) const
|
|
{
|
|
return getCreature()->getPosition_p();
|
|
}
|
|
|
|
Vector AICreatureController::getCreaturePosition_w ( void ) const
|
|
{
|
|
return getCreature()->getPosition_w();
|
|
}
|
|
|
|
float AICreatureController::getCreatureRadius ( void ) const
|
|
{
|
|
return getCreature()->getRadius();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::moveCreature( CellProperty const * newCell, Vector const & inNewPosition, bool testMove )
|
|
{
|
|
if(testMove)
|
|
{
|
|
CanMoveResult result = CollisionWorld::canMove(getCreature(),inNewPosition,0.05f,false,true,false);
|
|
|
|
if((result == CMR_MoveOK) || (result == CMR_HitObstacle))
|
|
{
|
|
m_stuckCounter = 0;
|
|
}
|
|
else
|
|
{
|
|
m_stuckCounter++;
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_stuckCounter = 0;
|
|
}
|
|
|
|
Vector newPosition = inNewPosition;
|
|
|
|
CellProperty * currentCell = getOwner()->getParentCell();
|
|
|
|
CellObject * newCellObject = NULL;
|
|
|
|
if( (newCell != NULL) && (newCell != CellProperty::getWorldCellProperty()) )
|
|
{
|
|
newCellObject = const_cast<CellObject*>(safe_cast<CellObject const *>(&newCell->getOwner()));
|
|
}
|
|
|
|
Vector oldPosition = getCreaturePosition_p();
|
|
|
|
// ----------
|
|
|
|
if(currentCell == newCell)
|
|
{
|
|
float dummy;
|
|
|
|
Vector offset(0.0f,1.0f,0.0f);
|
|
|
|
CellProperty const * destCell = getCreatureCell()->getDestinationCell(oldPosition+offset,newPosition+offset,dummy);
|
|
|
|
if((destCell != NULL) && (destCell != newCell))
|
|
{
|
|
newPosition = CollisionUtils::transformToCell(newCell,newPosition,destCell);
|
|
newCell = destCell;
|
|
|
|
newCellObject = NULL;
|
|
|
|
if(newCell != CellProperty::getWorldCellProperty())
|
|
{
|
|
newCellObject = const_cast<CellObject*>(safe_cast<CellObject const *>(&newCell->getOwner()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentCell == newCell)
|
|
{
|
|
Transform objectToCell = getOwner()->getTransform_o2p();
|
|
objectToCell.setPosition_p(newPosition);
|
|
setGoal(objectToCell, newCellObject);
|
|
}
|
|
else
|
|
{
|
|
Transform objectToWorld = getOwner()->getTransform_o2w();
|
|
|
|
Transform cellToWorld = newCellObject ? newCellObject->getTransform_o2w() : Transform::identity;
|
|
|
|
Transform worldToCell;
|
|
|
|
worldToCell.invert(cellToWorld);
|
|
|
|
Transform objectToCell;
|
|
objectToCell.multiply(worldToCell, objectToWorld);
|
|
|
|
objectToCell.setPosition_p(newPosition);
|
|
|
|
setGoal(objectToCell, newCellObject);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool stepTowards(Vector const & startPosition, Vector const & goalPosition, float stepDistance, Vector & newPosition)
|
|
{
|
|
bool reachedGoal = true;
|
|
Vector directionToGoal = Vector(goalPosition.x, 0.0f, goalPosition.z) - Vector(startPosition.x, 0.0f, startPosition.z);
|
|
float const distanceToGoalSquared = directionToGoal.magnitudeSquared();
|
|
|
|
if (distanceToGoalSquared <= sqr(stepDistance))
|
|
{
|
|
// We have overshot our goal, so just snap to the goal position
|
|
|
|
newPosition = goalPosition;
|
|
}
|
|
else
|
|
{
|
|
// We are not to our goal yet so step towards it
|
|
|
|
reachedGoal = false;
|
|
|
|
// Create a unit vector towards the goal
|
|
|
|
directionToGoal.normalize();
|
|
|
|
// Move along the unit vector the requested distance
|
|
|
|
directionToGoal *= stepDistance;
|
|
|
|
newPosition = startPosition + directionToGoal;
|
|
}
|
|
|
|
return reachedGoal;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::moveAlongGround( CellProperty const * targetCell, Vector const & target, float stepDistance)
|
|
{
|
|
if (stepDistance <= 0)
|
|
return true;
|
|
|
|
CellProperty const * creatureCell = getOwner()->getParentCell();
|
|
Vector localTarget = CollisionUtils::transformToCell( targetCell, target, creatureCell );
|
|
Vector creaturePos = getOwner()->getPosition_p();
|
|
Vector stepGoal;
|
|
|
|
bool reachedGoal = stepTowards(creaturePos,localTarget,stepDistance,stepGoal);
|
|
|
|
|
|
// For warping to work properly even when we are not in a pathfinding region
|
|
// we still need to warp if we are in a behavior like follow or flee.
|
|
// Not warping in these cases was causing ai to get stuck in Tansarii and would
|
|
// also cause problems anywhere there is not a CityPathGraphManager
|
|
AiMovementType curMoveType = AMT_invalid;
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
curMoveType = m_movement->getType();
|
|
}
|
|
|
|
bool testCollisions = m_inPathfindingRegion || curMoveType == AMT_follow || curMoveType == AMT_flee;
|
|
|
|
moveCreature(creatureCell,stepGoal,testCollisions);
|
|
|
|
return reachedGoal;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void AICreatureController::turnToward( CellProperty const * targetCell, Vector const & target )
|
|
{
|
|
CellProperty const * creatureCell = getOwner()->getParentCell();
|
|
|
|
Vector localTarget = CollisionUtils::transformToCell( targetCell, target, creatureCell );
|
|
|
|
Vector result = getOwner()->rotateTranslate_p2o(localTarget);
|
|
|
|
float angle = result.theta();
|
|
|
|
getOwner()->yaw_o(angle);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getGroundDistance( Vector const & A, Vector const & B )
|
|
{
|
|
float dx = A.x - B.x;
|
|
float dz = A.z - B.z;
|
|
|
|
return sqrt(dx*dx+dz*dz);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
void AICreatureController::setMovementRun()
|
|
{
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::setMovementRun() owner(%s)", getDebugInformation().c_str()));
|
|
|
|
m_running = true;
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetMovementRun);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
void AICreatureController::setMovementWalk()
|
|
{
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::setMovementWalk() owner(%s)", getDebugInformation().c_str()));
|
|
|
|
m_running = false;
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetMovementWalk);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::warpTo( CellProperty const * newCell, Vector const & newPosition )
|
|
{
|
|
DEBUG_WARNING((!newCell || newCell == CellProperty::getWorldCellProperty()) && newPosition == Vector::zero, ("AICreatureController::warpTo tried to send a creature to the origin."));
|
|
|
|
moveCreature(newCell,newPosition,false);
|
|
|
|
CollisionWorld::objectWarped(getOwner());
|
|
|
|
m_lastStartPosition = newPosition;
|
|
m_lastEndPosition = newPosition;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::sleep ( float sleepTime )
|
|
{
|
|
m_sleepTimer = sleepTime;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool AICreatureController::shouldHibernate ( void ) const
|
|
{
|
|
if (m_movement != AiMovementBaseNullPtr && !m_movement->getHibernateOk())
|
|
return false;
|
|
|
|
if (m_pendingMovement != AiMovementBaseNullPtr)
|
|
return false;
|
|
|
|
if (isRetreating())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return CreatureController::shouldHibernate();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setHibernate(bool hibernate)
|
|
{
|
|
bool oldHibernate = getHibernate();
|
|
float oldHibernationTimer = m_hibernationTimer.get();
|
|
if (!oldHibernate && hibernate && m_hibernationDelay.get() > 0 && oldHibernationTimer != 0)
|
|
{
|
|
// check if we want to start our hibernation timer
|
|
if (oldHibernationTimer < 0)
|
|
{
|
|
m_hibernationTimer = m_hibernationDelay.get();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreatureController::setHibernate(hibernate);
|
|
bool newHibernate = getHibernate();
|
|
|
|
// if we're coming out of hibernation, reset our alter time
|
|
if (!newHibernate && oldHibernate)
|
|
{
|
|
// flag the hibernate timer as ready to use
|
|
if (m_hibernationDelay.get() > 0)
|
|
m_hibernationTimer = -1.0f;
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
(safe_cast<ServerObject*>(getOwner()))->setDefaultAlterTime(m_movement->getDefaultAlterTime());
|
|
else
|
|
(safe_cast<ServerObject*>(getOwner()))->setDefaultAlterTime(AlterResult::cms_keepNoAlter);
|
|
}
|
|
}
|
|
if (hibernate && !oldHibernate && !getHibernate() && oldHibernationTimer <= 0 && m_hibernationTimer.get() > 0)
|
|
ObjectTracker::addDelayedHibernatingAI();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setHibernationDelay(float delay)
|
|
{
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
if (delay < 0)
|
|
delay = 0;
|
|
m_hibernationDelay = delay;
|
|
float oldTimer = m_hibernationTimer.get();
|
|
if (delay > 0)
|
|
{
|
|
if (!getHibernate() && oldTimer == 0)
|
|
{
|
|
// flag us as ready to delay the next time we want to hibernate
|
|
m_hibernationTimer = -1.0f;
|
|
}
|
|
else
|
|
{
|
|
// we need to set the hibernation timer > than the delay to flag skipping the first alter, which will be a large number
|
|
delay += 1.0f;
|
|
m_hibernationTimer = delay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!getHibernate() && oldTimer > 0)
|
|
ObjectTracker::removeDelayedHibernatingAI();
|
|
m_hibernationTimer = 0;
|
|
}
|
|
// see if we need to change our hibernate state
|
|
if (oldTimer > 0 && delay == 0)
|
|
updateHibernate();
|
|
else if (delay > 0 && getHibernate())
|
|
{
|
|
setHibernate(false);
|
|
updateHibernate();
|
|
// updateHibernate() can modify m_hibernationTimer, so we have to set it again
|
|
m_hibernationTimer = delay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_setHibernationDelay, new MessageQueueGenericValueType<float>(delay));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int AICreatureController::getStuckCounter ( void ) const
|
|
{
|
|
return m_stuckCounter;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setAuthority ( bool newAuthority )
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::setAuthority() authority(%s) inCombat(%s) owner(%s) ", newAuthority ? "yes" : "no", getCreature()->isInCombat() ? "yes" : "no", getDebugInformation().c_str()));
|
|
|
|
// if we are being made authoritative, refresh our ai behavior
|
|
if (newAuthority && !m_authority && m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
m_movement->refresh();
|
|
}
|
|
m_authority = newAuthority;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::forwardServerObjectSpecificBaselines() const
|
|
{
|
|
AiCreatureStateMessage aiCreatureStateMessage;
|
|
aiCreatureStateMessage.m_networkId = getOwner()->getNetworkId();
|
|
aiCreatureStateMessage.m_movement = AiMovementMessage(getOwner()->getNetworkId(), m_movement);
|
|
|
|
GenericValueTypeMessage<AiCreatureStateMessage> const genericMessage("AiCreatureStateMessage", aiCreatureStateMessage);
|
|
ServerMessageForwarding::send(genericMessage);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
typedef std::map< int, std::string > TriggerNameLookup;
|
|
|
|
char const * const getStringForTrigger( int trigger )
|
|
{
|
|
static TriggerNameLookup lookup;
|
|
static bool initialized = false;
|
|
|
|
if(!initialized)
|
|
{
|
|
#define add(A) lookup[static_cast<int>(A)] = #A
|
|
|
|
add( TRIG_WANDER_MOVING );
|
|
add( TRIG_WANDER_WAYPOINT );
|
|
add( TRIG_WANDER_WAITING );
|
|
add( TRIG_WANDER_PATH_NOT_FOUND );
|
|
add( TRIG_LOITER_MOVING );
|
|
add( TRIG_LOITER_WAYPOINT );
|
|
add( TRIG_LOITER_WAITING );
|
|
add( TRIG_FOLLOW_TARGET_LOST );
|
|
add( TRIG_FOLLOW_WAITING );
|
|
add( TRIG_FOLLOW_MOVING );
|
|
add( TRIG_FOLLOW_PATH_NOT_FOUND );
|
|
add( TRIG_FLEE_TARGET_LOST );
|
|
add( TRIG_FLEE_WAYPOINT );
|
|
add( TRIG_FLEE_PATH_NOT_FOUND );
|
|
add( TRIG_MOVE_PATH_COMPLETE );
|
|
add( TRIG_MOVE_MOVING );
|
|
add( TRIG_MOVE_PATH_NOT_FOUND );
|
|
add( TRIG_MADE_AUTHORITATIVE );
|
|
|
|
#undef add
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
return lookup[trigger].c_str();
|
|
}
|
|
|
|
void AICreatureController::triggerScriptsSimple( int intId )
|
|
{
|
|
TrigId id = static_cast<TrigId>(intId);
|
|
|
|
//LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::triggerScriptsSimple : %s\n", getStringForTrigger(intId)));
|
|
|
|
ScriptParams params;
|
|
IGNORE_RETURN(getServerOwner()->getScriptObject()->trigAllScripts(id, params));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::triggerScriptsWithTarget( int intId, NetworkId const & targetId )
|
|
{
|
|
//LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::triggerScriptsWithTarget : %s\n", getStringForTrigger(intId)));
|
|
|
|
TrigId id = static_cast<TrigId>(intId);
|
|
|
|
ScriptParams params;
|
|
params.addParam(targetId);
|
|
IGNORE_RETURN(getServerOwner()->getScriptObject()->trigAllScripts(id, params));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::triggerScriptsWithParam ( int intId, float & io_param )
|
|
{
|
|
//LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::triggerScriptsWithParam: %s, %f\n", getStringForTrigger(intId),io_param));
|
|
|
|
TrigId id = static_cast<TrigId>(intId);
|
|
|
|
ScriptParams params;
|
|
params.addParam(io_param);
|
|
IGNORE_RETURN(getServerOwner()->getScriptObject()->trigAllScripts(id, params));
|
|
io_param = params.getFloatParam(0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
AICreatureController * AICreatureController::getAiCreatureController(NetworkId const & networkId)
|
|
{
|
|
Object * const object = NetworkIdManager::getObjectById(networkId);
|
|
|
|
return (object != NULL) ? AICreatureController::asAiCreatureController(object->getController()) : NULL;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
AICreatureController * AICreatureController::asAiCreatureController(Controller * controller)
|
|
{
|
|
CreatureController * const creatureController = (controller != NULL) ? controller->asCreatureController() : NULL;
|
|
AICreatureController * const aiCreatureController = (creatureController != NULL) ? creatureController->asAiCreatureController() : NULL;
|
|
|
|
return aiCreatureController;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
AICreatureController const * AICreatureController::asAiCreatureController(Controller const * controller)
|
|
{
|
|
CreatureController const * const creatureController = (controller != NULL) ? controller->asCreatureController() : NULL;
|
|
AICreatureController const * const aiCreatureController = (creatureController != NULL) ? creatureController->asAiCreatureController() : NULL;
|
|
|
|
return aiCreatureController;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
AICreatureController * AICreatureController::asAiCreatureController()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
AICreatureController const * AICreatureController::asAiCreatureController() const
|
|
{
|
|
return this;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getAggroRadius() const
|
|
{
|
|
float result = ConfigServerGame::getAiBaseAggroRadius();
|
|
|
|
if (m_aiCreatureData->m_aggressive > 0.0f)
|
|
{
|
|
result = m_aiCreatureData->m_aggressive;
|
|
}
|
|
|
|
return clamp(0.0f, result, ConfigServerGame::getAiMaxAggroRadius());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getRespectRadius(NetworkId const & target) const
|
|
{
|
|
float const aggroRadius = getAggroRadius();
|
|
float result = aggroRadius;
|
|
|
|
CreatureObject const * respectCreatureObject = CreatureObject::getCreatureObject(target);
|
|
|
|
if (respectCreatureObject != NULL)
|
|
{
|
|
// If the target has a master, then use the master's level for the respect calculation
|
|
|
|
NetworkId const & masterId = respectCreatureObject->getMasterId();
|
|
|
|
if (masterId != NetworkId::cms_invalid)
|
|
{
|
|
respectCreatureObject = CreatureObject::getCreatureObject(masterId);
|
|
}
|
|
|
|
// Respect is only towards players
|
|
|
|
if ( (respectCreatureObject != NULL)
|
|
&& respectCreatureObject->isPlayerControlled())
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
int const ownerLevel = creatureOwner->getLevel();
|
|
int const targetLevel = respectCreatureObject->getLevel();
|
|
float const levelDifference = static_cast<float>(targetLevel) - static_cast<float>(ownerLevel);
|
|
float const respectPercent = clamp(0.04f, (1.0f - (levelDifference / 20.0f)), 1.20f);
|
|
|
|
result = respectPercent * aggroRadius;
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::getRespectRadius() owner(%s) master(%s) The owner has a master who is not a player. Why does this happen?", getDebugInformation().c_str(), (respectCreatureObject != NULL) ? respectCreatureObject->getDebugInformation().c_str() : masterId.getValueString().c_str()));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getAssistRadius() const
|
|
{
|
|
float assistRadius = ConfigServerGame::getAiAssistRadius();
|
|
|
|
if (m_aiCreatureData->m_assist > 0.0f)
|
|
{
|
|
assistRadius = m_aiCreatureData->m_assist;
|
|
}
|
|
|
|
return clamp(0.0f, assistRadius, ConfigServerGame::getAiMaxAggroRadius());
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::sendDebugAiToClients(AiDebugString & aiDebugString)
|
|
{
|
|
ServerObject * const serverOwner = getServerOwner();
|
|
const ObserverList & observerList = serverOwner->getObservers();
|
|
|
|
if (serverOwner->isAuthoritative() && !observerList.empty())
|
|
{
|
|
CreatureObject * const creatureOwner = serverOwner->asCreatureObject();
|
|
FormattedString<512> fs;
|
|
|
|
// Display the name and level
|
|
{
|
|
aiDebugString.addText(fs.sprintf("%d %s\n", creatureOwner->getLevel(), (getCreatureName().isEmpty() ? "NULL" : getCreatureName().getString())), PackedRgb::solidWhite);
|
|
}
|
|
|
|
// Display the look at target
|
|
//{
|
|
// aiDebugString.addText(fs.sprintf("lookAtTarget(%s)\n", creatureOwner->getLookAtTarget().getValueString().c_str()), PackedRgb::solidWhite);
|
|
//}
|
|
|
|
// Display the trigger volumes
|
|
//{
|
|
// aiDebugString.addText("Trigger Volumes\n", PackedRgb::solidWhite);
|
|
// ServerObject::TriggerVolumeMap::const_iterator iterTriggerVolumes = creatureOwner->getTriggerVolumeMap().begin();
|
|
// int index = 1;
|
|
//
|
|
// for (; iterTriggerVolumes != creatureOwner->getTriggerVolumeMap().end(); ++iterTriggerVolumes)
|
|
// {
|
|
// aiDebugString.addText(fs.sprintf("%i %s\n", index, iterTriggerVolumes->first.c_str()), PackedRgb::solidWhite);
|
|
// ++index;
|
|
// }
|
|
//}
|
|
|
|
if (creatureOwner->isInvulnerable())
|
|
{
|
|
aiDebugString.addText("INVULNERABLE\n", PackedRgb::solidMagenta);
|
|
}
|
|
|
|
if (isFrozen())
|
|
{
|
|
aiDebugString.addText("FROZEN\n", PackedRgb::solidCyan);
|
|
}
|
|
|
|
if (creatureOwner->isDead())
|
|
{
|
|
aiDebugString.addText("DEAD\n", PackedRgb::solidRed);
|
|
}
|
|
else
|
|
{
|
|
if (isRetreating())
|
|
{
|
|
int const retreatingTime = static_cast<int>(Os::getRealSystemTime() - m_retreatingStartTime.get());
|
|
|
|
aiDebugString.addText(fs.sprintf("RETREATING %d\n", retreatingTime), PackedRgb::solidMagenta);
|
|
}
|
|
|
|
// Show the floorId
|
|
{
|
|
Floor const * const floor = CollisionWorld::getFloorStandingOn(*creatureOwner);
|
|
|
|
aiDebugString.addText(fs.sprintf("floor (%s)\n", (floor == NULL) ? "none" : floor->getId().getValueString().c_str()), PackedRgb::solidWhite);
|
|
}
|
|
|
|
// Display the AI movement speed
|
|
{
|
|
float const movementPercent = (creatureOwner->getMovementPercent() * 100.0f);
|
|
|
|
aiDebugString.addText(fs.sprintf("mv (%.0f%%:%.1f)%s%s%s\n", movementPercent, getCurrentVelocity().magnitude(), (creatureOwner->isStopped() ? " stopped" : ""), (creatureOwner->isWalking() ? " walking" : ""), (creatureOwner->isRunning() ? " running" : "")), PackedRgb::solidWhite);
|
|
aiDebugString.addText(fs.sprintf("walk %.1f run %.1f\n", creatureOwner->getWalkSpeed(), creatureOwner->getRunSpeed()), PackedRgb::solidWhite);
|
|
}
|
|
|
|
Vector const & ownerPosition_w = serverOwner->getPosition_w();
|
|
|
|
if (creatureOwner->isInCombat())
|
|
{
|
|
// Primary Weapon
|
|
{
|
|
ServerObject const * const primaryServerObject = ServerObject::getServerObject(getPrimaryWeapon());
|
|
WeaponObject const * const primaryWeaponObject = (primaryServerObject != NULL) ? primaryServerObject->asWeaponObject() : NULL;
|
|
|
|
if (primaryWeaponObject != NULL)
|
|
{
|
|
aiDebugString.addText(fs.sprintf("%s pri(%s) [%.0f...%.0f] sp(%s)\n", (usingPrimaryWeapon() ? "->" : ""), FileNameUtils::get(primaryWeaponObject->getObjectTemplateName(), FileNameUtils::fileName).c_str(), primaryWeaponObject->getMinRange(), primaryWeaponObject->getMaxRange(), m_aiCreatureData->m_primarySpecials.isEmpty() ? "none" : m_aiCreatureData->m_primarySpecials.getString()), PackedRgb::solidWhite);
|
|
}
|
|
else
|
|
{
|
|
aiDebugString.addText(fs.sprintf("pri(NULL:ERROR)\n"), PackedRgb::solidWhite);
|
|
}
|
|
|
|
//if (usingPrimaryWeapon())
|
|
//{
|
|
//float const ownerRadius = creatureOwner->getRadius();
|
|
//float const minRange = primaryWeaponObject->getMinRange() + ownerRadius;
|
|
//float const maxRange = primaryWeaponObject->getMaxRange() + ownerRadius;
|
|
//
|
|
//if (minRange > ownerRadius)
|
|
//{
|
|
// aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, minRange, PackedRgb::solidMagenta);
|
|
//}
|
|
//
|
|
//if (maxRange > ownerRadius)
|
|
//{
|
|
// aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, maxRange, PackedRgb::solidRed);
|
|
//}
|
|
//}
|
|
}
|
|
|
|
// Secondary Weapon
|
|
{
|
|
ServerObject const * const secondaryServerObject = ServerObject::getServerObject(getSecondaryWeapon());
|
|
WeaponObject const * const secondaryWeaponObject = (secondaryServerObject != NULL) ? secondaryServerObject->asWeaponObject() : NULL;
|
|
|
|
if (secondaryWeaponObject != NULL)
|
|
{
|
|
aiDebugString.addText(fs.sprintf("%s sec (%s) [%.0f...%.0f] sp(%s)\n", (usingSecondaryWeapon() ? "->" : ""), FileNameUtils::get(secondaryWeaponObject->getObjectTemplateName(), FileNameUtils::fileName).c_str(), secondaryWeaponObject->getMinRange(), secondaryWeaponObject->getMaxRange(), m_aiCreatureData->m_secondarySpecials.isEmpty() ? "none" : m_aiCreatureData->m_secondarySpecials.getString()), PackedRgb::solidWhite);
|
|
}
|
|
else
|
|
{
|
|
aiDebugString.addText(fs.sprintf("sec (NONE)\n"), PackedRgb::solidWhite);
|
|
}
|
|
|
|
//if (usingSecondaryWeapon())
|
|
//{
|
|
//float const ownerRadius = creatureOwner->getRadius();
|
|
//float const minRange = secondaryWeaponObject->getMinRange() + ownerRadius;
|
|
//float const maxRange = secondaryWeaponObject->getMaxRange() + ownerRadius;
|
|
//
|
|
//if (minRange > ownerRadius)
|
|
//{
|
|
// aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, minRange, PackedRgb::solidMagenta);
|
|
//}
|
|
//
|
|
//if (maxRange > ownerRadius)
|
|
//{
|
|
// aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, maxRange, PackedRgb::solidRed);
|
|
//}
|
|
//}
|
|
}
|
|
|
|
// Display the leash length
|
|
{
|
|
aiDebugString.addLineToPosition(CellProperty::getPosition_w(m_combatStartLocation.get()), PackedRgb::solidGreen);
|
|
//aiDebugString.addCircle(m_combatStartLocation.get().getCoordinates(), getLeashRadius(), PackedRgb::solidGreen);
|
|
|
|
//float const leashLength = m_combatStartLocation.get().getCoordinates().magnitudeBetween(ownerPosition_w);
|
|
//float const leashRemaining = (floorf(getLeashRadius() - leashLength) / 2.0f) * 2.0f;
|
|
//aiDebugString.addText(fs.sprintf("leashRemaining(%.0fm)\n", leashRemaining), PackedRgb::solidCyan);
|
|
}
|
|
|
|
{
|
|
aiDebugString.addText(fs.sprintf("combat duration (%d)\n", creatureOwner->getCombatDuration()), PackedRgb::solidWhite);
|
|
}
|
|
|
|
// Display the AI hate list
|
|
{
|
|
//aiDebugString.addText(fs.sprintf("in combat: %s\n", (creatureOwner->isInCombat() ? "YES" : "NO")), PackedRgb::solidWhite);
|
|
|
|
HateList::SortedList hateList;
|
|
creatureOwner->getSortedHateList(hateList);
|
|
|
|
if (hateList.empty())
|
|
{
|
|
aiDebugString.addText("* NO HATE TARGETS *\n", PackedRgb::solidYellow);
|
|
}
|
|
else
|
|
{
|
|
if (creatureOwner->isHateListAutoExpireTargetEnabled())
|
|
{
|
|
aiDebugString.addText(fs.sprintf("* HATE LIST * %d\n", creatureOwner->getHateListAutoExpireTargetDuration() - creatureOwner->getTimeSinceLastHateListUpdate()), PackedRgb::solidRed);
|
|
}
|
|
else
|
|
{
|
|
aiDebugString.addText("* HATE LIST * no expire\n", PackedRgb::solidRed);
|
|
}
|
|
|
|
HateList::SortedList::const_iterator iterHateList = hateList.begin();
|
|
|
|
for (; iterHateList != hateList.end(); ++iterHateList)
|
|
{
|
|
CachedNetworkId const & hateTarget = iterHateList->first;
|
|
TangibleObject const * const hateTargetTangibleObject = TangibleObject::asTangibleObject(hateTarget.getObject());
|
|
float const hate = iterHateList->second;
|
|
std::string hateTargetName;
|
|
if (hateTargetTangibleObject != NULL)
|
|
{
|
|
hateTargetName = Unicode::wideToNarrow(hateTargetTangibleObject->getEncodedObjectName()).c_str();
|
|
}
|
|
else
|
|
{
|
|
hateTargetName = "NULL";
|
|
}
|
|
|
|
aiDebugString.addText(fs.sprintf("%s:%s(%.1f)\n", hateTargetName.c_str(), hateTarget.getValueString().c_str(), hate), (iterHateList == hateList.begin()) ? PackedRgb::solidGreen : PackedRgb::solidRed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isAggressive())
|
|
{
|
|
aiDebugString.addText(fs.sprintf("Aggro %.0fm\n", getAggroRadius()), PackedRgb::solidRed);
|
|
aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, getAggroRadius(), PackedRgb::solidRed);
|
|
}
|
|
|
|
if (isAssist())
|
|
{
|
|
aiDebugString.addText(fs.sprintf("Assist %.0fm\n", getAssistRadius()), PackedRgb::solidOrange);
|
|
aiDebugString.addCircleAtObjectOffset(creatureOwner->getNetworkId(), Vector::zero, getAssistRadius(), PackedRgb::solidOrange);
|
|
}
|
|
|
|
//if (isStalker())
|
|
//{
|
|
// aiDebugString.addText("S", PackedRgb::solidYellow);
|
|
//}
|
|
|
|
if (isKiller())
|
|
{
|
|
aiDebugString.addText("DeathBlow", PackedRgb::solidYellow);
|
|
}
|
|
}
|
|
|
|
// Line to server position
|
|
|
|
aiDebugString.addLineToPosition(ownerPosition_w, PackedRgb::solidYellow);
|
|
}
|
|
|
|
std::string finalString;
|
|
uint32 const crc = aiDebugString.toString(finalString);
|
|
|
|
if (m_aiDebugStringCrc != crc)
|
|
{
|
|
m_aiDebugStringCrc = crc;
|
|
|
|
ObserverList::const_iterator iterObserverList = observerList.begin();
|
|
|
|
for (; iterObserverList != observerList.end(); ++iterObserverList)
|
|
{
|
|
Client * const client = NON_NULL(*iterObserverList);
|
|
ServerObject * const characterObject = client->getCharacterObject();
|
|
|
|
// Only send to the client if the objvar is set
|
|
|
|
int temp = 0;
|
|
if ( (characterObject != NULL)
|
|
&& characterObject->getObjVars().getItem("ai_debug_string", temp))
|
|
{
|
|
if (temp > 0)
|
|
{
|
|
bool const reliable = true;
|
|
GenericValueTypeMessage<std::pair<NetworkId, std::string> > message("AiDebugString", std::make_pair(getOwner()->getNetworkId(), finalString));
|
|
|
|
client->send(message, reliable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// ----------------------------------------------------------------------
|
|
void AICreatureController::setHomeLocation(Location const & location)
|
|
{
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::setHomeLocation() owner(%s) sceneId(%u:%s) cell(%s) coordinates(%.2f, %.2f, %.2f)", getDebugInformation().c_str(), location.getSceneIdCrc(), (location.getSceneId() != NULL) ? location.getSceneId() : "NULL", location.getCell().getValueString().c_str(), location.getCoordinates().x, location.getCoordinates().y, location.getCoordinates().z));
|
|
|
|
m_homeLocation = location;
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetHomeLocation, new MessageQueueGenericValueType<Location>(location));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
Location const & AICreatureController::getHomeLocation() const
|
|
{
|
|
return m_homeLocation.get();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
void AICreatureController::markCombatStartLocation()
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
m_combatStartLocation = getLocation(*creatureOwner);
|
|
|
|
LOGC(AiLogManager::isLogging(creatureOwner->getNetworkId()), "debug_ai", ("AICreatureController::markCombatStartLocation() owner(%s) sceneId(%u:%s) cell(%s) coordinates(%.2f, %.2f, %.2f)", getDebugInformation().c_str(), m_combatStartLocation.get().getSceneIdCrc(), (m_combatStartLocation.get().getSceneId() != NULL) ? m_combatStartLocation.get().getSceneId() : "NULL", m_combatStartLocation.get().getCell().getValueString().c_str(), m_combatStartLocation.get().getCoordinates().x, m_combatStartLocation.get().getCoordinates().y, m_combatStartLocation.get().getCoordinates().z));
|
|
|
|
m_primaryWeaponActions.reset();
|
|
m_secondaryWeaponActions.reset();
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiMarkCombatStartLocation);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Location const & AICreatureController::getCombatStartLocation() const
|
|
{
|
|
return m_combatStartLocation.get();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float AICreatureController::getLeashRadius()
|
|
{
|
|
return ConfigServerGame::getAiLeashRadius();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setCreatureName(std::string const & creatureName)
|
|
{
|
|
if (getCreature()->isAuthoritative())
|
|
{
|
|
m_creatureName = creatureName;
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetCreatureName, new MessageQueueGenericValueType<std::string>(creatureName));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::onCreatureNameChanged(std::string const & creatureName)
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
// creatureName should be the creature name as defined in creatures_datatable.xml
|
|
|
|
m_aiCreatureData = &AiCreatureData::getCreatureData(TemporaryCrcString(creatureName.c_str(), false));
|
|
|
|
if (creatureOwner && creatureOwner->isAuthoritative())
|
|
{
|
|
setPrimaryWeapon(m_aiCreatureData->m_primaryWeapon);
|
|
setSecondaryWeapon(m_aiCreatureData->m_secondaryWeapon);
|
|
creatureOwner->setDifficulty(m_aiCreatureData->m_difficulty);
|
|
}
|
|
|
|
AiCreatureCombatProfile const * const primaryWeaponCombatProfile = AiCreatureCombatProfile::getCombatProfile(m_aiCreatureData->m_primarySpecials);
|
|
|
|
if (primaryWeaponCombatProfile != NULL)
|
|
{
|
|
m_primaryWeaponActions.setCombatProfile(*creatureOwner, *primaryWeaponCombatProfile);
|
|
}
|
|
|
|
AiCreatureCombatProfile const * const secondaryWeaponCombatProfile = AiCreatureCombatProfile::getCombatProfile(m_aiCreatureData->m_secondarySpecials);
|
|
|
|
if (secondaryWeaponCombatProfile != NULL)
|
|
{
|
|
m_secondaryWeaponActions.setCombatProfile(*creatureOwner, *secondaryWeaponCombatProfile);
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(creatureOwner->getNetworkId()), "debug_ai", ("AICreatureController::setCreatureName() owner(%s) creatureName(%s) level(%d) primaryWeapon(%s:%s) secondaryWeapon(%s:%s)", getDebugInformation().c_str(), creatureName.c_str(), creatureOwner->getLevel(), m_primaryWeapon.get().getValueString().c_str(), m_aiCreatureData->m_primaryWeapon.getString(), m_secondaryWeapon.get().getValueString().c_str(), m_aiCreatureData->m_secondaryWeapon.getString()));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setPrimaryWeapon(CrcString const & objectTemplate)
|
|
{
|
|
bool const required = true;
|
|
NetworkId const newPrimaryWeapon = createWeapon("setPrimaryWeapon", objectTemplate, required);
|
|
|
|
if (newPrimaryWeapon == NetworkId::cms_invalid)
|
|
{
|
|
WARNING(true, ("AICreatureController::setPrimaryWeapon() owner(%s) Unable to create the requested primary weapon(%s)", getDebugInformation().c_str(), objectTemplate.getString()));
|
|
}
|
|
else
|
|
{
|
|
bool const primaryWeaponEquipped = usingPrimaryWeapon();
|
|
|
|
destroyPrimaryWeapon();
|
|
|
|
m_primaryWeapon = newPrimaryWeapon;
|
|
|
|
if (primaryWeaponEquipped)
|
|
{
|
|
equipPrimaryWeapon();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::destroyPrimaryWeapon()
|
|
{
|
|
WeaponObject * const primaryWeapon = WeaponObject::getWeaponObject(getPrimaryWeapon());
|
|
|
|
if (primaryWeapon != NULL)
|
|
{
|
|
unEquipWeapons();
|
|
primaryWeapon->permanentlyDestroy(DeleteReasons::God);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::setSecondaryWeapon(CrcString const & objectTemplate)
|
|
{
|
|
bool const required = false;
|
|
NetworkId const newSecondaryWeapon = createWeapon("setSecondaryWeapon", objectTemplate, required);
|
|
|
|
if (newSecondaryWeapon != NetworkId::cms_invalid)
|
|
{
|
|
bool const secondaryWeaponEquipped = usingSecondaryWeapon();
|
|
|
|
destroySecondaryWeapon();
|
|
|
|
m_secondaryWeapon = newSecondaryWeapon;
|
|
|
|
if (secondaryWeaponEquipped)
|
|
{
|
|
equipSecondaryWeapon();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void AICreatureController::destroySecondaryWeapon()
|
|
{
|
|
WeaponObject * const secondaryWeapon = WeaponObject::getWeaponObject(getSecondaryWeapon());
|
|
|
|
if (secondaryWeapon != NULL)
|
|
{
|
|
unEquipWeapons();
|
|
secondaryWeapon->permanentlyDestroy(DeleteReasons::God);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
PersistentCrcString const & AICreatureController::getCreatureName() const
|
|
{
|
|
if (m_aiCreatureData->m_name == NULL)
|
|
{
|
|
return *s_defaultCreatureName;
|
|
}
|
|
|
|
return *m_aiCreatureData->m_name;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
NetworkId const & AICreatureController::getPrimaryWeapon() const
|
|
{
|
|
return m_primaryWeapon.get();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
NetworkId const & AICreatureController::getSecondaryWeapon() const
|
|
{
|
|
return m_secondaryWeapon.get();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
NetworkId AICreatureController::createWeapon(char const * const functionName, CrcString const & weaponName, bool const required)
|
|
{
|
|
NetworkId result;
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
ServerObject * const inventory = creatureOwner->getInventory();
|
|
|
|
if (inventory == NULL)
|
|
{
|
|
WARNING(true, ("AICreatureController::%s() Unable to find inventory for (%s) to place a weapon(%s)", functionName, getDebugInformation().c_str(), weaponName.getString()));
|
|
}
|
|
else
|
|
{
|
|
if (weaponName == TemporaryCrcString("none", false))
|
|
{
|
|
if (required)
|
|
{
|
|
WARNING(required, ("debug_ai: AICreatureController::%s() A required weapon is specified as ""none"" for (%s), defaulting to ""unarmed""", functionName, getDebugInformation().c_str()));
|
|
result = getUnarmedWeapon();
|
|
}
|
|
else
|
|
{
|
|
result = NetworkId::cms_invalid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = getUnarmedWeapon();
|
|
|
|
if (weaponName != TemporaryCrcString("unarmed", false))
|
|
{
|
|
std::string const weaponTemplateName(AiCreatureData::getWeaponTemplateName(weaponName));
|
|
|
|
if (weaponTemplateName != "unarmed")
|
|
{
|
|
ConstCharCrcString const weaponCrcName(ObjectTemplateList::lookUp(weaponTemplateName.c_str()));
|
|
|
|
if (weaponCrcName.getCrc() == 0)
|
|
{
|
|
WARNING(true, ("AICreatureController::%s() Unable to resolve a weapon(%s) crc for (%s)", functionName, weaponTemplateName.c_str(), getDebugInformation().c_str()));
|
|
}
|
|
else
|
|
{
|
|
bool const persisted = false;
|
|
ServerObject * const newObject = ServerWorld::createNewObject(weaponCrcName.getCrc(), *inventory, persisted);
|
|
|
|
if (newObject != NULL)
|
|
{
|
|
result = newObject->getNetworkId();
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::%s() Unable to create a WeaponObject(%s) for (%s)", functionName, weaponTemplateName.c_str(), getDebugInformation().c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
NetworkId AICreatureController::getUnarmedWeapon()
|
|
{
|
|
NetworkId result;
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
WeaponObject * const defaultWeapon = creatureOwner->getDefaultWeapon();
|
|
|
|
if (defaultWeapon != NULL)
|
|
{
|
|
result = defaultWeapon->getNetworkId();
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::getUnarmedWeapon() Unable to find a default weapon for (%s)", getDebugInformation().c_str()));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::equipPrimaryWeapon()
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
if (!isCombatAi())
|
|
{
|
|
WARNING(true, ("AICreatureController::equipPrimaryWeapon() owner(%s) A non-combat AI is trying to equip a weapon", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
if (!usingPrimaryWeapon())
|
|
{
|
|
ServerObject * const primaryWeaponServerObject = ServerObject::getServerObject(getPrimaryWeapon());
|
|
WeaponObject * const primaryWeaponObject = (primaryWeaponServerObject != NULL) ? primaryWeaponServerObject->asWeaponObject() : NULL;
|
|
|
|
if (primaryWeaponObject != NULL)
|
|
{
|
|
unEquipWeapons();
|
|
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (primaryWeaponObject == creatureOwner->getDefaultWeapon())
|
|
{
|
|
creatureOwner->setCurrentWeapon(*primaryWeaponObject);
|
|
}
|
|
else
|
|
{
|
|
Container::ContainerErrorCode errorCode;
|
|
|
|
if (ContainerInterface::transferItemToGeneralContainer(*creatureOwner, *primaryWeaponServerObject, NULL, errorCode))
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::equipPrimaryWeapon() owner(%s) primaryWeapon(%s)\n", getDebugInformation().c_str(), primaryWeaponServerObject->getDebugInformation().c_str()));
|
|
|
|
creatureOwner->setCurrentWeapon(*primaryWeaponObject);
|
|
|
|
GameScriptObject * const gameScriptObject = GameScriptObject::asGameScriptObject(creatureOwner);
|
|
|
|
if (gameScriptObject != NULL)
|
|
{
|
|
ScriptParams scriptParams;
|
|
scriptParams.addParam(primaryWeaponObject->getNetworkId());
|
|
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_AI_PRIMARY_WEAPON_EQUIPPED, scriptParams));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::equipPrimaryWeapon() owner(%s) Transfer of primaryWeapon(%s) to the visible equipped slot failed(%d).", getDebugInformation().c_str(), primaryWeaponServerObject->getDebugInformation().c_str(), errorCode));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::equipPrimaryWeapon() owner(%s) Unable to resolve primaryWeapon(%s:%s) to a WeaponObject.", getDebugInformation().c_str(), m_primaryWeapon.get().getValueString().c_str(), m_aiCreatureData->m_primaryWeapon.getString()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiEquipPrimaryWeapon);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::equipSecondaryWeapon()
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
if (!isCombatAi())
|
|
{
|
|
WARNING(true, ("AICreatureController::equipSecondaryWeapon() owner(%s) A non-combat AI is trying to equip a weapon", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
if (!usingSecondaryWeapon())
|
|
{
|
|
ServerObject * const secondaryWeaponServerObject = ServerObject::getServerObject(getSecondaryWeapon());
|
|
WeaponObject * const secondaryWeaponObject = (secondaryWeaponServerObject != NULL) ? secondaryWeaponServerObject->asWeaponObject() : NULL;
|
|
|
|
if (secondaryWeaponObject != NULL)
|
|
{
|
|
unEquipWeapons();
|
|
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (secondaryWeaponObject == creatureOwner->getDefaultWeapon())
|
|
{
|
|
creatureOwner->setCurrentWeapon(*secondaryWeaponObject);
|
|
}
|
|
else
|
|
{
|
|
Container::ContainerErrorCode errorCode;
|
|
|
|
if (ContainerInterface::transferItemToGeneralContainer(*creatureOwner, *secondaryWeaponServerObject, NULL, errorCode))
|
|
{
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::equipSecondaryWeapon() owner(%s) secondaryWeapon(%s) errorCode(%d)\n", getDebugInformation().c_str(), secondaryWeaponServerObject->getDebugInformation().c_str()));
|
|
|
|
creatureOwner->setCurrentWeapon(*secondaryWeaponObject);
|
|
|
|
GameScriptObject * const gameScriptObject = GameScriptObject::asGameScriptObject(creatureOwner);
|
|
|
|
if (gameScriptObject != NULL)
|
|
{
|
|
ScriptParams scriptParams;
|
|
scriptParams.addParam(secondaryWeaponObject->getNetworkId());
|
|
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_AI_SECONDARY_WEAPON_EQUIPPED, scriptParams));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::equipSecondaryWeapon() owner(%s) Transfer of secondaryWeapon(%s) to the visible equipped slot failed(%d).", getDebugInformation().c_str(), secondaryWeaponServerObject->getDebugInformation().c_str(), errorCode));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::equipSecondaryWeapon() owner(%s) Unable to resolve secondaryWeapon(%s:%s) to a WeaponObject.", getDebugInformation().c_str(), m_secondaryWeapon.get().getValueString().c_str(), m_aiCreatureData->m_secondaryWeapon.getString()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiEquipSecondaryWeapon);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::unEquipWeapons()
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
if (!isCombatAi())
|
|
{
|
|
WARNING(true, ("AICreatureController::unEquipWeapons() owner(%s) A non-combat AI is trying to unequip a weapon", getDebugInformation().c_str()));
|
|
return;
|
|
}
|
|
|
|
LOGC(AiLogManager::isLogging(getOwner()->getNetworkId()), "debug_ai", ("AICreatureController::unEquipWeapons() owner(%s)\n", getDebugInformation().c_str()));
|
|
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
WeaponObject * const currentWeaponObject = creatureOwner->getCurrentWeapon();
|
|
WeaponObject * const defaultWeapon = creatureOwner->getDefaultWeapon();
|
|
|
|
if (currentWeaponObject != defaultWeapon)
|
|
{
|
|
// Put the current weapon back in the inventory
|
|
{
|
|
ServerObject * const inventory = creatureOwner->getInventory();
|
|
|
|
if (inventory != NULL)
|
|
{
|
|
Container::ContainerErrorCode error;
|
|
|
|
if (!ContainerInterface::transferItemToVolumeContainer(*inventory, *currentWeaponObject, creatureOwner, error))
|
|
{
|
|
WARNING(true, ("AICreatureController::unEquipWeapons() owner(%s) Transfer of the currentWeapon(%s) to the inventory failed(%d).", getDebugInformation().c_str(), currentWeaponObject->getDebugInformation().c_str(), error));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::unEquipWeapons() owner(%s) Unable to find an inventory.", getDebugInformation().c_str()));
|
|
}
|
|
}
|
|
|
|
// Equip the default weapon
|
|
{
|
|
if (defaultWeapon != NULL)
|
|
{
|
|
creatureOwner->setCurrentWeapon(*defaultWeapon);
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::unEquipWeapons() owner(%s) NULL default weapon", getDebugInformation().c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiUnEquipWeapons);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::hasPrimaryWeapon() const
|
|
{
|
|
bool const result = (getPrimaryWeapon() != NetworkId::cms_invalid);
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::hasSecondaryWeapon() const
|
|
{
|
|
bool const result = (getSecondaryWeapon() != NetworkId::cms_invalid);
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::usingPrimaryWeapon()
|
|
{
|
|
bool result = false;
|
|
|
|
if (hasPrimaryWeapon())
|
|
{
|
|
WeaponObject const * const currentWeaponObject = getCreature()->getCurrentWeapon();
|
|
|
|
if (currentWeaponObject != NULL)
|
|
{
|
|
result = (getPrimaryWeapon() == currentWeaponObject->getNetworkId());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::usingSecondaryWeapon()
|
|
{
|
|
bool result = false;
|
|
|
|
if (hasSecondaryWeapon() && !usingPrimaryWeapon())
|
|
{
|
|
WeaponObject const * const currentWeaponObject = getCreature()->getCurrentWeapon();
|
|
|
|
if (currentWeaponObject != NULL)
|
|
{
|
|
result = (getSecondaryWeapon() == currentWeaponObject->getNetworkId());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::setFrozen(bool const frozen)
|
|
{
|
|
if (getOwner()->isAuthoritative())
|
|
{
|
|
m_frozen = frozen;
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetFrozen, new MessageQueueGenericValueType<bool>(frozen));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isFrozen() const
|
|
{
|
|
return m_frozen.get();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
float AICreatureController::getMovementSpeedPercent() const
|
|
{
|
|
return m_aiCreatureData->m_movementSpeedPercent;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::addServerNpAutoDeltaVariables(Archive::AutoDeltaByteStream & stream)
|
|
{
|
|
stream.addVariable(m_running);
|
|
stream.addVariable(m_homeLocation);
|
|
stream.addVariable(m_primaryWeapon);
|
|
stream.addVariable(m_secondaryWeapon);
|
|
stream.addVariable(m_frozen);
|
|
stream.addVariable(m_combatStartLocation);
|
|
stream.addVariable(m_retreating);
|
|
stream.addVariable(m_retreatingStartTime);
|
|
stream.addVariable(m_logging);
|
|
stream.addVariable(m_creatureName);
|
|
stream.addVariable(m_hibernationDelay);
|
|
stream.addVariable(m_hibernationTimer);
|
|
|
|
m_primaryWeaponActions.addServerNpAutoDeltaVariables(stream);
|
|
m_secondaryWeaponActions.addServerNpAutoDeltaVariables(stream);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isAggressive() const
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
|
|
return (!creatureOwner->isInvulnerable() && (m_aiCreatureData->m_aggressive > 0.0f));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isAssist() const
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
|
|
return (!creatureOwner->isInvulnerable() && (m_aiCreatureData->m_assist > 0.0f));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isStalker() const
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
|
|
return (!creatureOwner->isInvulnerable() && m_aiCreatureData->m_stalker);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isKiller() const
|
|
{
|
|
CreatureObject const * const creatureOwner = getCreature();
|
|
|
|
return (!creatureOwner->isInvulnerable() && (m_aiCreatureData->m_deathBlow == AiCreatureData::DB_instant));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::setRetreating(bool const retreating)
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
bool const retreatStart = !m_retreating.get() && retreating;
|
|
bool const retreatComplete = m_retreating.get() && !retreating;
|
|
|
|
if (retreatStart)
|
|
{
|
|
// Start the AI towards its anchor position
|
|
|
|
NetworkId const & cellId = m_combatStartLocation.get().getCell();
|
|
Vector const & position_c = m_combatStartLocation.get().getCoordinates();
|
|
|
|
if (cellId != NetworkId::cms_invalid)
|
|
{
|
|
CellObject const * const cellObject = CellObject::getCellObject(cellId);
|
|
|
|
if (cellObject != NULL)
|
|
{
|
|
CellProperty const * const cellProperty = cellObject->getCellProperty();
|
|
|
|
moveTo(cellProperty, position_c);
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("AICreatureController::setRetreating() ai(%s) Unable to resolve the cellId(%s) to a CellObject", creatureOwner->getDebugInformation().c_str(), cellId.getValueString().c_str()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
moveTo(CellProperty::getWorldCellProperty(), position_c);
|
|
}
|
|
}
|
|
|
|
m_retreating = retreating;
|
|
|
|
if (retreatStart)
|
|
{
|
|
m_retreatingStartTime = Os::getRealSystemTime();
|
|
|
|
creatureOwner->clearHateList();
|
|
|
|
triggerScriptsSimple(TRIG_AI_RETREAT_START);
|
|
}
|
|
else if (retreatComplete)
|
|
{
|
|
triggerScriptsSimple(TRIG_AI_RETREAT_COMPLETE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetRetreating, new MessageQueueGenericValueType<bool>(retreating));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::setLogging(bool const logging)
|
|
{
|
|
CreatureObject * const creatureOwner = getCreature();
|
|
|
|
if (creatureOwner->isAuthoritative())
|
|
{
|
|
m_logging = logging;
|
|
|
|
AiLogManager::setLogging(creatureOwner->getNetworkId(), logging);
|
|
}
|
|
else
|
|
{
|
|
sendMessageToAuthServer(CM_aiSetLogging, new MessageQueueGenericValueType<bool>(logging));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isLogging() const
|
|
{
|
|
return m_logging.get();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isRetreating() const
|
|
{
|
|
return m_retreating.get();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool AICreatureController::isCombatAi() const
|
|
{
|
|
return (m_aiCreatureData != &AiCreatureData::getDefaultCreatureData());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
std::string AICreatureController::getDebugInformation() const
|
|
{
|
|
return FormattedString<1024>().sprintf("creatureName=%s %s", getCreatureName().getString(), getOwner()->getDebugInformation().c_str());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::sendMessageToAuthServer(GameControllerMessage const gameControllerMessage)
|
|
{
|
|
int const flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER;
|
|
|
|
appendMessage(gameControllerMessage, 0, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::sendMessageToAuthServer(GameControllerMessage const gameControllerMessage, MessageQueue::Data * messageQueue)
|
|
{
|
|
int const flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER;
|
|
|
|
appendMessage(gameControllerMessage, 0, messageQueue, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void AICreatureController::sendMessageToProxyServer(GameControllerMessage const gameControllerMessage, MessageQueue::Data * messageQueue)
|
|
{
|
|
int const flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_SERVER;
|
|
|
|
appendMessage(gameControllerMessage, 0, messageQueue, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
AiMovementType AICreatureController::getMovementType() const
|
|
{
|
|
AiMovementType result = AMT_invalid;
|
|
|
|
if (m_movement != AiMovementBaseNullPtr)
|
|
{
|
|
result = m_movement->getType();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
AiMovementType AICreatureController::getPendingMovementType() const
|
|
{
|
|
AiMovementType result = AMT_invalid;
|
|
|
|
if (m_pendingMovement != AiMovementBaseNullPtr)
|
|
{
|
|
result = m_pendingMovement->getType();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
PersistentCrcString const & AICreatureController::getCombatAction()
|
|
{
|
|
if (usingPrimaryWeapon())
|
|
{
|
|
return m_primaryWeaponActions.getCombatAction();
|
|
}
|
|
else if (usingSecondaryWeapon())
|
|
{
|
|
return m_secondaryWeaponActions.getCombatAction();
|
|
}
|
|
|
|
return PersistentCrcString::empty;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
time_t AICreatureController::getKnockDownRecoveryTime() const
|
|
{
|
|
AiCreatureCombatProfile const * const combatProfile = AiCreatureCombatProfile::getCombatProfile(m_aiCreatureData->m_primarySpecials);
|
|
|
|
return (combatProfile != NULL) ? combatProfile->m_knockDownRecoveryTime : 0;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
std::string const AICreatureController::getCombatActionsString()
|
|
{
|
|
std::string result;
|
|
FormattedString<4096> fs;
|
|
|
|
result += fs.sprintf("knockDownRecoveryTime: %u\n", static_cast<unsigned int>(getKnockDownRecoveryTime()));
|
|
|
|
// Primary Weapon
|
|
{
|
|
WeaponObject const * const primaryWeaponObject = WeaponObject::getWeaponObject(getPrimaryWeapon());
|
|
|
|
if (primaryWeaponObject != NULL)
|
|
{
|
|
result += fs.sprintf("PRIMARY WEAPON >>> %s range[%.0f...%.0f] %s\n", FileNameUtils::get(primaryWeaponObject->getObjectTemplateName(), FileNameUtils::fileName).c_str(), primaryWeaponObject->getMinRange(), primaryWeaponObject->getMaxRange(), usingPrimaryWeapon() ? "(active)" : "");
|
|
}
|
|
else
|
|
{
|
|
result += fs.sprintf("PRIMARY WEAPON >>> ERROR NO WEAPON\n");
|
|
}
|
|
|
|
AiCreatureCombatProfile const * const primaryWeaponCombatProfile = AiCreatureCombatProfile::getCombatProfile(m_aiCreatureData->m_primarySpecials);
|
|
if (primaryWeaponCombatProfile != NULL)
|
|
{
|
|
result += primaryWeaponCombatProfile->toString();
|
|
}
|
|
else
|
|
{
|
|
result += fs.sprintf(" no special attacks\n");
|
|
}
|
|
}
|
|
|
|
// Secondary Weapon
|
|
{
|
|
WeaponObject const * const secondaryWeaponObject = WeaponObject::getWeaponObject(getSecondaryWeapon());
|
|
|
|
if (secondaryWeaponObject != NULL)
|
|
{
|
|
result += fs.sprintf("SECONDARY WEAPON >>> %s range[%.0f...%.0f] %s\n", FileNameUtils::get(secondaryWeaponObject->getObjectTemplateName(), FileNameUtils::fileName).c_str(), secondaryWeaponObject->getMinRange(), secondaryWeaponObject->getMaxRange(), usingSecondaryWeapon() ? "(active)" : "");
|
|
}
|
|
else
|
|
{
|
|
result += fs.sprintf("SECONDARY WEAPON >>> NONE\n");
|
|
}
|
|
|
|
AiCreatureCombatProfile const * const secondaryWeaponCombatProfile = AiCreatureCombatProfile::getCombatProfile(m_aiCreatureData->m_secondarySpecials);
|
|
|
|
if (secondaryWeaponCombatProfile != NULL)
|
|
{
|
|
result += secondaryWeaponCombatProfile->toString();
|
|
}
|
|
else
|
|
{
|
|
result += fs.sprintf(" no special attacks\n");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ======================================================================
|