Files
src-1.2/engine/server/library/serverGame/src/shared/object/CreatureObject.cpp
T
2014-01-15 03:59:18 -07:00

15909 lines
538 KiB
C++

//========================================================================
//
// CreatureObject.cpp
// Copyright 2001-2005, Sony Online Entertainment, Inc.
// All rights reserved.
//
//========================================================================
#include "serverGame/FirstServerGame.h"
#pragma warning(disable : 4100 4514)
#include "serverGame/CreatureObject.h"
#include "SwgGameServer/CombatEngine.h"
#include "SwgGameServer/ConfigCombatEngine.h"
#include "UnicodeUtils.h"
#include "serverGame/AiCreatureController.h"
#include "serverGame/AttribModNameManager.h"
#include "serverGame/BiographyManager.h"
#include "serverGame/BuffManager.h"
#include "serverGame/BuildingObject.h"
#include "serverGame/CellObject.h"
#include "serverGame/Chat.h"
#include "serverGame/CitizenInfo.h"
#include "serverGame/CityInfo.h"
#include "serverGame/CityInterface.h"
#include "serverGame/Client.h"
#include "serverGame/CommandCppFuncs.h"
#include "serverGame/CommandQueue.h"
#include "serverGame/ConfigServerGame.h"
#include "serverGame/ConsoleManager.h"
#include "serverGame/ContainerInterface.h"
#include "serverGame/DraftSchematicObject.h"
#include "serverGame/GameServer.h"
#include "serverGame/GroupObject.h"
#include "serverGame/GroupMissionCriticalObjectsBuilder.h"
#include "serverGame/GroupStringId.h"
#include "serverGame/GuildInterface.h"
#include "serverGame/GuildMemberInfo.h"
#include "serverGame/GuildObject.h"
#include "serverGame/ServerImageDesignerManager.h"
#include "serverGame/ManufactureSchematicObject.h"
#include "serverGame/MessageToQueue.h"
#include "serverGame/MissionObject.h"
#include "serverGame/MoveSimManager.h"
#include "serverGame/NameManager.h"
#include "serverGame/NewbieTutorial.h"
#include "serverGame/NpcConversation.h"
#include "serverGame/ObjectTracker.h"
#include "serverGame/ObserveTracker.h"
#include "serverGame/PlanetObject.h"
#include "serverGame/PlayerCreatureController.h"
#include "serverGame/PlayerObject.h"
#include "serverGame/PvpInternal.h"
#include "serverGame/Region.h"
#include "serverGame/RegionMaster.h"
#include "serverGame/ReportManager.h"
#include "serverGame/ServerCreatureObjectTemplate.h"
#include "serverGame/ServerMessageForwarding.h"
#include "serverGame/ServerMissionObjectTemplate.h"
#include "serverGame/ServerSecureTrade.h"
#include "serverGame/ServerUniverse.h"
#include "serverGame/ServerWeaponObjectTemplate.h"
#include "serverGame/ServerWorld.h"
#include "serverGame/ShipObject.h"
#include "serverGame/SpaceVisibilityManager.h"
#include "serverGame/TriggerVolume.h"
#include "serverGame/VeteranRewardManager.h"
#include "serverGame/WeaponObject.h"
#include "serverNetworkMessages/AccountFeatureIdResponse.h"
#include "serverNetworkMessages/FeatureIdTransactionRequest.h"
#include "serverNetworkMessages/FeatureIdTransactionResponse.h"
#include "serverNetworkMessages/GroupMemberParam.h"
#include "serverNetworkMessages/LoginUpgradeAccountMessage.h"
#include "serverNetworkMessages/MessageQueueAlterAttribute.h"
#include "serverNetworkMessages/MessageQueueCommandQueueEnqueueFwd.h"
#include "serverNetworkMessages/MessageQueueSetState.h"
#include "serverNetworkMessages/MessageToPayload.h"
#include "serverNetworkMessages/TransferReplyMoveValidation.h"
#include "serverNetworkMessages/UnloadedPlayerMessage.h"
#include "serverNetworkMessages/UpdateObjectOnPlanetMessage.h"
#include "serverNetworkMessages/UploadCharacterMessage.h"
#include "serverScript/GameScriptObject.h"
#include "serverScript/JavaLibrary.h"
#include "serverScript/ScriptDictionary.h"
#include "serverScript/ScriptParameters.h"
#include "serverUtility/AdminAccountManager.h"
#include "serverUtility/ServerClock.h"
#include "Session/CommonAPI/CommonAPI.h"
#include "sharedCollision/BaseExtent.h"
#include "sharedCollision/CollisionProperty.h"
#include "sharedCollision/CollisionWorld.h"
#include "sharedCollision/Floor.h"
#include "sharedCollision/Footprint.h"
#include "sharedDebug/Profiler.h"
#include "sharedFile/TreeFile.h"
#include "sharedFoundation/CalendarTime.h"
#include "sharedFoundation/ConfigFile.h"
#include "sharedFoundation/ConstCharCrcLowerString.h"
#include "sharedFoundation/Clock.h"
#include "sharedFoundation/ConfigFile.h"
#include "sharedFoundation/ConstCharCrcString.h"
#include "sharedFoundation/Crc.h"
#include "sharedFoundation/DynamicVariableList.h"
#include "sharedFoundation/DynamicVariableListNestedList.h"
#include "sharedFoundation/DynamicVariableLocationData.h"
#include "sharedFoundation/FormattedString.h"
#include "sharedFoundation/GameControllerMessage.h"
#include "sharedFoundation/TemporaryCrcString.h"
#include "sharedGame/AppearanceManager.h"
#include "sharedGame/AttribModArchive.h"
#include "sharedGame/Buff.h"
#include "sharedGame/CityData.h"
#include "sharedGame/CommandTable.h"
#include "sharedGame/ConfigSharedGame.h"
#include "sharedGame/CraftingData.h"
#include "sharedGame/CustomizationManager.h"
#include "sharedGame/CustomizationManager_MorphParameter.h"
#include "sharedGame/GameObjectTypes.h"
#include "sharedGame/LfgDataTable.h"
#include "sharedGame/MatchMakingCharacterResult.h"
#include "sharedGame/OutOfBandPackager.h"
#include "sharedGame/PlatformFeatureBits.h"
#include "sharedGame/PlayerCreationManager.h"
#include "sharedGame/ProsePackage.h"
#include "sharedGame/PvpData.h"
#include "sharedGame/SharedCreatureObjectTemplate.h"
#include "sharedGame/SharedStringIds.h"
#include "sharedGame/ShipSlotIdManager.h"
#include "sharedGame/SlopeEffectProperty.h"
#include "sharedGame/SlowDownProperty.h"
#include "sharedGame/SpatialChatManager.h"
#include "sharedGame/VehicleHoverDynamics.h"
#include "sharedLog/Log.h"
#include "sharedMath/AxialBox.h"
#include "sharedMath/MxCifQuadTreeBounds.h"
#include "sharedMath/Ray3d.h"
#include "sharedNetworkMessages/ClientMfdStatusUpdateMessage.h"
#include "sharedNetworkMessages/ErrorMessage.h"
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
#include "sharedNetworkMessages/GrantCommand.h"
#include "sharedNetworkMessages/MessageQueueCombatSpam.h"
#include "sharedNetworkMessages/MessageQueueCommandTimer.h"
#include "sharedNetworkMessages/MessageQueueDataTransformWithParent.h"
#include "sharedNetworkMessages/MessageQueueGenericValueType.h"
#include "sharedNetworkMessages/MessageQueueMissionListResponse.h"
#include "sharedNetworkMessages/MessageQueueOpponentInfo.h"
#include "sharedNetworkMessages/MessageQueuePosture.h"
#include "sharedNetworkMessages/MessageQueuePushCreature.h"
#include "sharedNetworkMessages/MessageQueueSitOnObject.h"
#include "sharedNetworkMessages/MessageQueueSlowDownEffect.h"
#include "sharedNetworkMessages/MessageQueueSpatialChat.h"
#include "sharedNetworkMessages/MessageQueueStringList.h"
#include "sharedNetworkMessages/RevokeCommand.h"
#include "sharedNetworkMessages/SceneChannelMessages.h"
#include "sharedNetworkMessages/SlowDownEffectMessage.h"
#include "serverNetworkMessages/TransferCharacterData.h"
#include "serverNetworkMessages/TransferCharacterDataArchive.h"
#include "sharedNetworkMessages/UpdatePostureMessage.h"
#include "sharedObject/AlterResult.h"
#include "sharedObject/CellProperty.h"
#include "sharedObject/ContainedByProperty.h"
#include "sharedObject/CustomizationData.h"
#include "sharedObject/MovementTable.h"
#include "sharedObject/NetworkIdManager.h"
#include "sharedObject/ObjectTemplateList.h"
#include "sharedObject/SlotId.h"
#include "sharedObject/SlotIdManager.h"
#include "sharedObject/SlottedContainer.h"
#include "sharedObject/SlottedContainmentProperty.h"
#include "sharedObject/VolumeContainer.h"
#include "sharedSkillSystem/ExpertiseManager.h"
#include "sharedSkillSystem/LevelManager.h"
#include "sharedSkillSystem/SkillManager.h"
#include "sharedSkillSystem/SkillObject.h"
#include "sharedTerrain/TerrainObject.h"
#include "sharedTerrain/WaterTypeManager.h"
#include "sharedUtility/DataTable.h"
#include "sharedUtility/DataTableManager.h"
#include "swgSharedNetworkMessages/MessageQueueCombatAction.h"
#include "swgSharedUtility/Attributes.h"
#include "swgSharedUtility/States.h"
#include "sharedUtility/ValueDictionary.h"
#include <algorithm>
#include <deque>
#include <hash_set>
#include <set>
//----------------------------------------------------------------------
// static CreatureObject vars
const SharedObjectTemplate * CreatureObject::m_defaultSharedTemplate = NULL;
//----------------------------------------------------------------------
// The max number of lots available to a player. This value is also defined in base_class.java
static const int HOUSING_MAX_LOTS = 10;
// Slot names
static const ConstCharCrcLowerString DATAPAD_SLOT_NAME("datapad");
// Crafting objvar names
static const std::string OBJVAR_DISABLE_SCHEMATIC_FILTER("crafting.disableSchematicFiltering");
//objvar for banking
static const std::string OBJVAR_BANK_ID("banking_bankid");
static const std::string OBJVAR_OPEN_BANK_TERMINAL_ID("open_bank_location");
//slot names
static const ConstCharCrcLowerString BANK_SLOT_NAME("bank");
static const ConstCharCrcLowerString MISSION_BAG_SLOT_NAME("mission_bag");
// skill data granted independently from skills
static const std::string OBJVAR_NOT_SKILL_MODS("_notskill.mods");
static const std::string OBJVAR_NOT_SKILL_COMMANDS("_notskill.commands");
static const std::string OBJVAR_NOT_SKILL_SCHEMATICS("_notskill.schematics");
// special skill mods
static const std::string SLOPE_MOD("slope_move");
static const std::string GROUP_SLOPE_MOD("group_slope_move");
// jedi/force power objvars
static const std::string OBJVAR_NEW_JEDI_FORCE("_start_force");
static const std::string OBJVAR_JEDI("jedi");
static const std::string OBJVAR_ADD_JEDI_ACK("jediAddedAcknowlege");
//static const std::string OBJVAR_FORCE_REGEN_RATE("jedi.regenrate");
//static const std::string OBJVAR_FORCE_REGEN_VALUE("jedi.regenvalue");
static const std::string OBJVAR_JEDI_VISIBILITY("jedi.visibility");
// deprecated badge objvars (will be used to move old badges over to the collection system)
static const std::string OBJVAR_BADGES("badge.tracking");
static const std::string OBJVAR_COUNT_BADGES("badge.count");
// residence house id objvar
const std::string OBJVAR_PLAYER_HOUSE_ID("residenceHouseId");
// If a pet accepts a group invitation but is deleted before
// being given the group information, we need to try to remove
// him from the group in the future
static const unsigned MAX_ATTEMPTS_TO_REMOVE_PET_FROM_GROUP = 30;
static const int TIME_BETWEEN_ATTEMPTS_TO_REMOVE_PET = 1;
// creature regen constants
const static std::string CREATURES_TABLE("datatables/mob/creatures.iff");
//----------------------------------------------------------------------
namespace CreatureObjectNamespace
{
const int POINTS_PER_TIER = 4;
// ----------------------------------------------------------------------
const char* s_inventoryTemplate = "object/tangible/inventory/character_inventory.iff";
const char* s_datapadTemplate = "object/tangible/datapad/character_datapad.iff";
const char* s_missionBagTemplate = "object/tangible/mission_bag/mission_bag.iff";
const char* s_bankTemplate = "object/tangible/bank/character_bank.iff";
const char* s_appearanceTemplate = "object/tangible/inventory/appearance_inventory.iff";
SlotId s_defaultWeaponSlotId = SlotId::invalid;
SlotId s_inventorySlotId = SlotId::invalid;
SlotId s_appearanceSlotId = SlotId::invalid;
bool s_allowNullDefaultWeapon = false;
// ----------------------------------------------------------------------
std::map<std::string, std::pair<int, int> > s_instrumentTypeMap;
// ----------------------------------------------------------------------
void loadInstrumentTable()
{
char const *fileName = ConfigServerGame::getInstrumentDataTableFilename();
DataTable *t = DataTableManager::getTable(fileName, true);
if (!t)
{
FATAL(true, ("Could not find instrument datatable %s.", fileName));
}
for (int row = 0; row < t->getNumRows(); ++row)
{
std::string name = t->getStringValue("serverTemplateName", row);
int visualId = t->getIntValue("instrumentVisualId", row);
int audioId = t->getIntValue("instrumentAudioId", row);
IGNORE_RETURN(s_instrumentTypeMap.insert(std::make_pair(name, std::make_pair(visualId, audioId))));
}
DataTableManager::close(fileName);
}
// ----------------------------------------------------------------------
int lookupInstrumentVisualId(char const * serverTemplateName)
{
std::map<std::string, std::pair<int, int> >::const_iterator i = s_instrumentTypeMap.find(std::string(serverTemplateName));
if (i != s_instrumentTypeMap.end())
return (*i).second.first;
return 0;
}
// ----------------------------------------------------------------------
int lookupInstrumentAudioId(char const * serverTemplateName)
{
std::map<std::string, std::pair<int, int> >::const_iterator i = s_instrumentTypeMap.find(std::string(serverTemplateName));
if (i != s_instrumentTypeMap.end())
return (*i).second.second;
return 0;
}
// ----------------------------------------------------------------------
std::map<int, int> s_danceVisualMap;
// ----------------------------------------------------------------------
void loadDanceVisualTable()
{
char const *fileName = ConfigServerGame::getPerformanceDataTableFilename();
DataTable *t = DataTableManager::getTable(fileName, true);
for (int row = 0; row < t->getNumRows(); ++row)
{
int visualId = t->getIntValue("danceVisualId", row);
if (visualId)
{
IGNORE_RETURN(s_danceVisualMap.insert(std::make_pair(row+1, visualId)));
}
}
DataTableManager::close(fileName);
}
struct CommandSeriesRecord
{
std::string commandName;
std::string baseCommandName;
int level;
};
std::vector<CommandSeriesRecord> s_commandSeriesRecords;
void loadCommandSeriesTable()
{
DataTable *t = DataTableManager::getTable("datatables/command/command_series.iff", true);
for (int row = 0; row < t->getNumRows(); ++row)
{
CommandSeriesRecord csr;
csr.commandName = t->getStringValue("commandName", row);
csr.baseCommandName = t->getStringValue("baseCommandName", row);
csr.level = t->getIntValue("level", row);
IGNORE_RETURN(s_commandSeriesRecords.push_back(csr));
}
DataTableManager::close("datatables/command/command_series.iff");
}
// ----------------------------------------------------------------------
int lookupDanceVisual(int performanceType)
{
std::map<int, int>::const_iterator i = s_danceVisualMap.find(performanceType);
if (i != s_danceVisualMap.end())
return (*i).second;
return 0;
}
// ----------------------------------------------------------------------
bool instrumentTooFar(Vector const &playerPos_w, Vector const &instrumentPos_w)
{
static float maxDistSquared = sqr(ConfigServerGame::getMaxInstrumentPlayDistance());
if (playerPos_w.magnitudeBetweenSquared(instrumentPos_w) < maxDistSquared)
return false;
return true;
}
// ----------------------------------------------------------------------
CreatureObject::AllCreaturesSet g_creatureList;
std::vector<std::pair<CreatureObject *, NetworkId> > gs_missionRequestQueue;
std::vector<std::pair<CreatureObject *, NetworkId> > gs_missionRequestQueueDeferredRemoves;
void removeCreatureFromMissionRequestQueue(const CreatureObject * creature)
{
std::vector<std::pair<CreatureObject *, NetworkId> >::iterator qiter;
for(qiter = gs_missionRequestQueue.begin(); qiter != gs_missionRequestQueue.end(); ++qiter)
{
if((*qiter).first == creature)
{
gs_missionRequestQueueDeferredRemoves.push_back((*qiter));
}
}
}
std::deque<NetworkId> s_spawnQueue;
std::deque<NetworkId> s_missionQueue;
//----------------------------------------------------------------------
//- @odo: these lists really need to be moved into C++
bool isBuildingAdminForTarget (const ServerObject & object, const ServerObject & target)
{
const TangibleObject* hopperObject = target.asTangibleObject();
const CreatureObject* player = object.asCreatureObject();
if (!player || !hopperObject)
return false;
return hopperObject->isOnAdminList(*player);
}
//----------------------------------------------------------------------
bool canHopperForTarget (const ServerObject & object, const ServerObject & target)
{
const TangibleObject* hopperObject = target.asTangibleObject();
const CreatureObject* player = object.asCreatureObject();
if (!player || !hopperObject)
return false;
return hopperObject->isOnHopperList(*player);
}
//----------------------------------------------------------------------
bool isTargetBuildingBaseObject (const ServerObject & buildingObject, const ServerObject & target)
{
static const std::string objvarname_baseObjects = "player_structure.base_objects";
const NetworkId & targetId = target.getNetworkId ();
typedef stdvector<NetworkId>::fwd NetworkIdVector;
static NetworkIdVector ids;
ids.clear ();
if (buildingObject.getObjVars ().getItem (objvarname_baseObjects, ids))
{
for (NetworkIdVector::const_iterator it = ids.begin (); it != ids.end (); ++it)
{
const NetworkId & id = *it;
if (id == targetId)
return true;
}
}
return false;
}
bool isOnVendorList(const CreatureObject& object, const ServerObject& target)
{
static const std::string objvarname_vendorList = "player_structure.vendor.vendorList";
const Unicode::String & requestorFirstName = object.getAssignedObjectFirstName ();
typedef stdvector<Unicode::String>::fwd StringVector;
static StringVector sv;
sv.clear ();
if (target.getObjVars ().getItem (objvarname_vendorList, sv))
{
for (StringVector::const_iterator it = sv.begin (); it != sv.end (); ++it)
{
const Unicode::String & vendorFirstName = *it;
if (Unicode::caseInsensitiveCompare (vendorFirstName, requestorFirstName))
{
return true;
}
}
}
return false;
}
//----------------------------------------------------------------------
namespace SlotNames
{
static const ConstCharCrcLowerString datapad ("datapad");
static const ConstCharCrcLowerString inventory ("inventory");
static const ConstCharCrcLowerString bank ("bank");
static const ConstCharCrcLowerString hair ("hair");
static const ConstCharCrcLowerString ghost ("ghost");
static const ConstCharCrcLowerString appearance ("appearance_inventory");
static const ConstCharCrcLowerString hangar ("hangar");
}
uint64 s_containmentStatesMask = 0;
std::map<SlotId, uint64> s_containerSlotStates;
namespace GroupHelpers
{
bool creatureIsContainedInPOBShip(CreatureObject const * creatureObject);
void findAllTargetsForGroup(CreatureObject * const targetObj, std::vector<CreatureObject *> & targets);
bool roomInGroup(GroupObject const * groupObj, int additionalMembers);
GroupMemberParam const buildGroupMemberParam(CreatureObject const * creatureObject);
void buildGroupMemberParamsFromCreatures(std::vector<CreatureObject *> const & targets, GroupObject::GroupMemberParamVector & targetMemberParams);
}
// ----------------------------------------------------------------------
void skipField(std::string const &source, unsigned int &pos)
{
while (source[pos] && source[pos] != '~')
++pos;
if (source[pos] == '~')
++pos;
}
// ----------------------------------------------------------------------
void nextString(std::string const &source, unsigned int &pos, std::string &ret)
{
unsigned int oldPos = pos;
while (source[pos] && source[pos] != '~')
++pos;
ret = source.substr(oldPos, pos-oldPos);
if (source[pos] == '~')
++pos;
}
// ----------------------------------------------------------------------
void nextOid(std::string const &source, unsigned int &pos, NetworkId &ret)
{
unsigned int oldPos = pos;
skipField(source, pos);
ret = NetworkId(source.substr(oldPos, pos-oldPos));
}
// ----------------------------------------------------------------------
void nextFloat(std::string const &source, unsigned int &pos, float &ret)
{
ret = static_cast<float>(atof(source.c_str()+pos));
skipField(source, pos);
}
// for enforcing height restriction when restoring decoration layout
std::map<std::pair<uint32, uint32>, std::pair<float, float> > s_mapCellHeightRestriction;
void restoreItemDecorationLayout(CreatureObject & decorator, PlayerObject & decoratorPlayerObject, CachedNetworkId const & item, TangibleObject const & pob, CachedNetworkId const & cell, Vector const & position, Quaternion const & orientation);
}
//=======================================================================
using namespace CreatureObjectNamespace;
//=======================================================================
void CreatureObject::PostureChangeCallback::modified(CreatureObject &target, Postures::Enumerator oldValue, Postures::Enumerator value, bool) const
{
UNREF(oldValue);
// Make creatures not collidable when they're in dead or incapacitated posture
CollisionProperty * collision = target.getCollisionProperty();
if (collision)
{
bool collidable = (value != Postures::Dead && value != Postures::Incapacitated);
collision->setCollidable(collidable);
}
//-- Retrieve the collection of avialable locomotions for fast/slow/stationary movement speeds.
// This info is used on authoritative and proxy objects to make calcLocomotions(), called every alter,
// considerably more efficient.
target.getAvailableLocomotions();
if (value == Postures::Dead)
Pvp::handleCreatureDied(target);
}
//=======================================================================
void CreatureObject::LocomotionChangeCallback::modified(CreatureObject &target, Locomotions::Enumerator oldValue, Locomotions::Enumerator value, bool) const
{
ScriptParams params;
params.addParam(value);
params.addParam(oldValue);
if (target.isAuthoritative())
{
GameScriptObject * const script = target.getScriptObject();
if (script != 0)
{
script->trigAllScripts(Scripting::TRIG_LOCOMOTION_CHANGED, params);
}
}
}
// ======================================================================
void CreatureObject::StateChangeCallback::modified(CreatureObject &target, uint64 oldValue, uint64 newValue, bool isLocal) const
{
UNREF(newValue);
UNREF(isLocal);
UNREF(oldValue);
//-- Retrieve the collection of avialable locomotions for fast/slow/stationary movement speeds.
// This info is used on authoritative and proxy objects to make calcLocomotions(), called every alter,
// considerably more efficient.
target.getAvailableLocomotions();
}
// ======================================================================
CreatureAttitude::CreatureAttitude() :
m_behavior(Behaviors::Invalid),
m_angerState(kAngerCalm),
m_fearState(kFearCalm)
{
for (int i = 0; i <MentalStates::NumberOfMentalStates; ++i)
m_currentValues[i] = 0;
}
// ----------------------------------------------------------------------
bool CreatureAttitude::operator!=(CreatureAttitude const &other) const
{
if ( m_behavior != other.m_behavior
|| m_angerState != other.m_angerState
|| m_fearState != other.m_fearState)
return true;
for (int i = 0; i < MentalStates::NumberOfMentalStates; ++i)
if (m_currentValues[i] != other.m_currentValues[i])
return true;
return false;
}
// ----------------------------------------------------------------------
float CreatureAttitude::operator-(CreatureAttitude const &other) const
{
float result = 0.0f;
if (m_behavior != other.m_behavior)
result = 30.0f;
if (m_angerState != other.m_angerState)
result += 30.0f;
if (m_fearState != other.m_fearState)
result += 30.0f;
for (int i = 0; i < MentalStates::NumberOfMentalStates; ++i)
result += abs(m_currentValues[i] - other.m_currentValues[i]);
return result;
}
// ======================================================================
MonitoredCreatureMovement::MonitoredCreatureMovement() :
m_lastDistance(0),
m_skittishness(0),
m_curve(0)
{
}
// ----------------------------------------------------------------------
bool MonitoredCreatureMovement::operator!=(MonitoredCreatureMovement const &other) const
{
return m_lastDistance != other.m_lastDistance
|| m_skittishness != other.m_skittishness
|| m_curve != other.m_curve;
}
// ----------------------------------------------------------------------
float MonitoredCreatureMovement::operator-(MonitoredCreatureMovement const &other) const
{
return abs(m_lastDistance - other.m_lastDistance)
+ abs(m_skittishness - other.m_skittishness)
+ abs(m_curve - other.m_curve);
}
// ======================================================================
CreatureObject::CreatureObject(const ServerCreatureObjectTemplate* newTemplate) :
TangibleObject(newTemplate),
m_commandQueue(NULL),
m_isStatic(false),
m_shield(NULL),
m_regenerationTime(0),
m_attributes(Attributes::NumberOfAttributes),
m_maxAttributes(Attributes::NumberOfAttributes),
m_totalAttributes(Attributes::NumberOfAttributes),
m_totalMaxAttributes(Attributes::NumberOfAttributes),
m_attribBonus(Attributes::NumberOfAttributes),
m_shockWounds(0),
m_attributeModList(),
m_cachedCurrentAttributeModValues(Attributes::NumberOfAttributes),
m_cachedMaxAttributeModValues(Attributes::NumberOfAttributes),
m_currentAttitude(),
m_lastBehavior(),
m_mentalStatesToward(),
m_maxMentalStates(),
m_mentalStateDecays(),
m_scaleFactor(1.0f),
m_monitoredCreatureMovements(),
m_guildId(0),
m_timeToUpdateGuildWarPvpStatus(0),
m_guildWarEnabled(false),
m_militiaOfCityId(),
m_locatedInCityId(),
m_masterId(),
m_rank(0),
m_wsX(0.0),
m_wsY(0.0),
m_wsZ(0.0),
// m_animState(CreatureObject::AS_idle),
m_mood(0),
m_sayMode(0),
m_animationMood("neutral"),
m_posture(Postures::Upright),
m_stopWalkRun(2),
m_states(0),
m_movementScale(1.0f),
m_movementPercent(1.0f),
m_baseWalkSpeed (-1.0f),
m_walkSpeed (safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_walk)),
m_baseRunSpeed (-1.0f),
m_runSpeed (safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_run)),
m_accelScale (1.0f),
m_accelPercent (1.0f),
m_turnScale (1.0f),
m_slopeModAngle (safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSlopeModAngle() * PI / 180),
m_baseSlopeModPercent (safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSlopeModPercent()),
m_slopeModPercent (1.0f),
m_waterModPercent (safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getWaterModPercent()),
m_lookAtTarget (),
m_intendedTarget (),
m_cover (0),
m_coverVisibility (true),
m_currentWeapon (),
m_modMap (),
m_skills (),
m_deferComputeTotalAttributes (0),
m_group (),
m_groupInviter (),
m_inviterForPendingGroup (),
m_performanceType (),
m_performanceStartTime (),
m_animatingSkillData (),
m_level (-1),
m_previousLevel (-1),
m_totalLevelXp (0),
m_levelHealthGranted (0),
m_invulnerabilityTimer (),
m_alternateAppearanceSharedObjectTemplateName (),
m_clientMfdStatusUpdateMessage(0),
m_timedMod (),
m_timedModDuration (),
m_timedModUpdateTime (),
m_addedToSpawnQueue (false),
m_numberOfMissionsWantedInMissionBag(0),
m_fastLocomotion (Locomotions::Invalid),
m_slowLocomotion (Locomotions::Invalid),
m_stationaryLocomotion (Locomotions::Invalid),
m_vehiclePhysicsData (0),
m_missionCriticalObjectSet(),
m_groupMissionCriticalObjectSet(),
m_triggerVolumeEntered(),
m_pseudoPlayedTime(0.0f),
m_buffs(),
m_persistedBuffs(),
m_clientUsesAnimationLocomotion(false),
m_difficulty(static_cast<unsigned char>(D_normal)),
m_hologramType(-1),
m_visibleOnMapAndRadar(true),
m_lookAtYaw(0.0f),
m_useLookAtYaw(false),
m_fixedupPersistentBuffsAfterLoading(false),
m_fixedupLevelXpAfterLoading(false),
m_lavaResistance(0.0f),
m_lastWaterDamageTime(0),
m_isBeast(false),
m_forceShowHam(false),
m_regionFlagTimer(ConfigServerGame::getRegionFlagUpdateTimeSecs()),
m_wearableAppearanceData(),
m_decoyOrigin(NetworkId::cms_invalid)
{
addProperty(*(new CommandQueue(*this)));
// get the shared object template
const SharedCreatureObjectTemplate * sharedTemplate = safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate());
NOT_NULL(sharedTemplate);
// Handle scale.
// setScaleFactor(safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getScale());
// Cannot set scale with setScaleFactor() since this object isn't authoratative yet. Expect many
// bugs from the lack of authority setting within a constructor.
const float scale = std::max (0.0f, sharedTemplate->getScale());
m_scaleFactor = scale;
setScale (Vector::xyz111 * scale);
// get the attribute values
{
for (int i = 0; i < Attributes::NumberOfAttributes; ++i)
{
m_attributes.set(i, static_cast<Attributes::Value>(
newTemplate->getAttributes(static_cast<
ServerCreatureObjectTemplate::Attributes>(i))));
m_maxAttributes.set(i, m_attributes.get(i));
m_cachedCurrentAttributeModValues.set(i, 0);
m_cachedMaxAttributeModValues.set(i, 0);
m_regeneration[i] = 0;
}
// store the default regen values in the former faucet attrib slots
m_attributes.set(Attributes::Constitution, static_cast<int>(ConfigServerGame::getDefaultHealthRegen()));
m_attributes.set(Attributes::Stamina, static_cast<int>(ConfigServerGame::getDefaultActionRegen()));
m_attributes.set(Attributes::Willpower, static_cast<int>(ConfigServerGame::getDefaultMindRegen()));
}
// fix the size of the mental state vectors
m_maxMentalStates.resize(MentalStates::NumberOfMentalStates, 0);
m_mentalStateDecays.resize(MentalStates::NumberOfMentalStates, 0);
CreatureAttitude initialState;
// get the mental state values
{
for (int i = 0; i < MentalStates::NumberOfMentalStates; ++i)
{
const ServerCreatureObjectTemplate::MentalStates state = static_cast<ServerCreatureObjectTemplate::MentalStates>(i);
m_maxMentalStates.set(i, static_cast<MentalStates::Value>(newTemplate->getMaxMentalStates(state)));
initialState.m_currentValues[i] = 0;
m_mentalStateDecays.set(i, float(newTemplate->getMentalStatesDecay(state)));
}
}
m_currentAttitude.set(initialState);
m_lastBehavior.set(-1);
m_fearCutoffs[CreatureAttitude::kFearCalm] = 0;
m_fearCutoffs[CreatureAttitude::kFearAlert] = 25;
m_fearCutoffs[CreatureAttitude::kFearThreaten] = 50;
m_fearCutoffs[CreatureAttitude::kFearFlee] = 75;
m_fearCutoffs[CreatureAttitude::kFearPanic] = 100;
m_angerCutoffs[CreatureAttitude::kAngerCalm] = 0;
m_angerCutoffs[CreatureAttitude::kAngerAttack] = 60;
m_angerCutoffs[CreatureAttitude::kAngerFrenzy] = 100;
CollisionProperty * pCollision = getCollisionProperty();
if(pCollision)
{
pCollision->setServerSide(true);
}
addMembersToPackages();
m_guildId.setSourceObject(this);
m_guildWarEnabled.setSourceObject(this);
m_militiaOfCityId.setSourceObject(this);
m_locatedInCityId.setSourceObject(this);
m_group.setSourceObject(this);
m_posture.setSourceObject(this);
m_locomotion.setSourceObject(this);
m_invulnerabilityTimer.setSourceObject(this);
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
CustomizationData *const cd = fetchCustomizationData();
if (cd)
{
VehicleHoverDynamics::setInitialParams (*cd,
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getSpeedMin (SharedCreatureObjectTemplate::MT_walk),
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getSpeed (SharedCreatureObjectTemplate::MT_run),
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_walk),
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_run),
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_walk),
safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_run));
cd->release ();
}
}
m_lavaResistance = WaterTypeManager::getLavaResistance(getObjectTemplateName());
IGNORE_RETURN(g_creatureList.insert(this));
ObjectTracker::addCreature();
}
//-----------------------------------------------------------------------
CreatureObject::~CreatureObject()
{
//-- This must be the first line in the destructor to invalidate any watchers watching this object
nullWatchers();
if (m_vehiclePhysicsData)
{
delete m_vehiclePhysicsData;
m_vehiclePhysicsData = 0;
}
if (isAuthoritative())
{
GroupObject *group = getGroup();
if (group)
{
group->removeGroupMember(getNetworkId());
}
else
{
// If we are a pet...
if ( getMasterId() != NetworkId::cms_invalid )
{
// We need to clean up outstanding group invitations since
// there are race conditions with ServerUniverse group
// objects and us getting added to them
// Check for an outstanding group formation
const NetworkId groupInviterNetworkId = getInviterForPendingGroup();
if ( groupInviterNetworkId != NetworkId::cms_invalid )
{
// Send a message to the group inviter to tell them to remove
// us from the group...
// We need to tell the network ID of the pet and we need to keep
// of how many times this message has been sent to the inviter
char msgBuffer[100];
sprintf( msgBuffer, "%s 1", getNetworkId().getValueString().c_str() );
// Send the message with a delay
if ( MessageToQueue::isInstalled() )
{
MessageToQueue::getInstance().sendMessageToC( groupInviterNetworkId,
"C++RemovePetFromGroup",
msgBuffer,
TIME_BETWEEN_ATTEMPTS_TO_REMOVE_PET,
false ); // reliable
}
}
}
}
if (getGuildId())
GuildInterface::onAboutToClearGuildId(*this);
}
if (isPlayerControlled())
removeFromSpawnQueue();
removeCreatureFromMissionRequestQueue(this);
if (isInWorld())
removeFromWorld();
Controller *controller = getController();
if (controller)
{
CreatureController *creatureController = safe_cast<CreatureController*>(controller);
ServerSecureTrade *trade = creatureController->getSecureTrade();
if (trade)
trade->cancelTrade(*this);
}
// AICreatureController * aiController = AICreatureController::asAiCreatureController(controller);
// if (aiController != NULL)
// {
// aiController->stop();
// }
IGNORE_RETURN(g_creatureList.erase(this));
delete m_clientMfdStatusUpdateMessage;
// We should have have been removed from all TriggerVolumes by
// now, but if we haven't, we need to tell any TriggerVolumes
// that we haven't been removed from to remove us from their
// contents list, or else those TriggerVolumes will have a
// pointer to a non-existent object
for (std::set<TriggerVolume *>::const_iterator i = m_triggerVolumeEntered.begin(); i != m_triggerVolumeEntered.end(); ++i)
{
WARNING(true, ("CreatureObject (%s) being destroyed but hasn't been removed from TriggerVolume (%s) owned by (%s)",
getDebugInformation().c_str(),
(*i)->getName().c_str(),
(*i)->getOwner().getDebugInformation().c_str()));
(*i)->objectDestroyed(*this);
}
ObjectTracker::removeCreature();
}
//-----------------------------------------------------------------------
CreatureObject * CreatureObject::asCreatureObject()
{
return this;
}
//-----------------------------------------------------------------------
CreatureObject const * CreatureObject::asCreatureObject() const
{
return this;
}
//-----------------------------------------------------------------------
void CreatureObject::install()
{
loadDanceVisualTable();
loadInstrumentTable();
loadCommandSeriesTable();
installForMounts();
s_defaultWeaponSlotId = SlotIdManager::findSlotId(ConstCharCrcLowerString("default_weapon"));
FATAL(s_defaultWeaponSlotId == SlotId::invalid, ("failed to find SlotId for default_weapon: the slot definition file was improperly loaded or modified."));
s_inventorySlotId = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::inventory);
FATAL(s_inventorySlotId == SlotId::invalid, ("failed to find SlotId for inventory: the slot definition file was improperly loaded or modified."));
s_appearanceSlotId = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::appearance);
FATAL(s_appearanceSlotId == SlotId::invalid, ("Failed to find SlotId for appearance: the slot definition file was improperly loaded or modified."));
s_containerSlotStates[ShipSlotIdManager::getShipPilotSlotId()] = States::getStateMask(States::PilotingShip);
s_containerSlotStates[ShipSlotIdManager::getPobShipPilotSlotId()] = States::getStateMask(States::PilotingPobShip);
s_containerSlotStates[ShipSlotIdManager::getShipOperationsSlotId()] = States::getStateMask(States::ShipOperations);
s_containerSlotStates[ShipSlotIdManager::getPobShipOperationsSlotId()] = States::getStateMask(States::ShipOperations);
{
// corresponds to slots in CreatureObject_Mounts.cpp
char slotName[256];
s_containerSlotStates[SlotIdManager::findSlotId(ConstCharCrcLowerString("rider"))] = States::getStateMask(States::RidingMount);
for (int i = 1; i < 8; ++i)
{
snprintf(slotName, 255, "rider%d", i);
SlotId const slot = SlotIdManager::findSlotId(ConstCharCrcLowerString(slotName));
s_containerSlotStates[slot] = States::getStateMask(States::RidingMount);
}
}
for (int weaponIndex = 0; weaponIndex < ShipChassisSlotType::cms_numWeaponIndices; ++weaponIndex)
{
s_containerSlotStates[ShipSlotIdManager::getShipGunnerSlotId(weaponIndex)] = States::getStateMask(States::ShipGunner);
s_containerSlotStates[ShipSlotIdManager::getPobShipGunnerSlotId(weaponIndex)] = States::getStateMask(States::ShipGunner);
}
s_containmentStatesMask = States::getStateMask(States::ShipInterior);
for (std::map<SlotId, uint64>::const_iterator i = s_containerSlotStates.begin(); i != s_containerSlotStates.end(); ++i)
s_containmentStatesMask |= (*i).second;
// for enforcing height restriction when restoring decoration layout
{
char const * const cellHeightDataTableName = "datatables/structure/cell_height.iff";
DataTable * table = DataTableManager::getTable(cellHeightDataTableName, true);
if (table)
{
int const columnStructure = table->findColumnNumber("template");
int const columnCell = table->findColumnNumber("cell");
int const columnMinHeight = table->findColumnNumber("min_height");
int const columnMaxHeight = table->findColumnNumber("max_height");
if ((columnStructure >= 0) && (columnCell >= 0) && (columnMinHeight >= 0) && (columnMaxHeight >= 0))
{
std::string structure;
std::string cell;
for (int i = 0, numRows = table->getNumRows(); i < numRows; ++i)
{
structure = table->getStringValue(columnStructure, i);
if (structure.empty())
continue;
cell = table->getStringValue(columnCell, i);
if (cell.empty())
continue;
s_mapCellHeightRestriction[std::make_pair(CrcLowerString::calculateCrc(structure.c_str()), CrcLowerString::calculateCrc(cell.c_str()))] = std::make_pair(table->getFloatValue(columnMinHeight, i), table->getFloatValue(columnMaxHeight, i));
}
}
DataTableManager::close(cellHeightDataTableName);
}
}
ExitChain::add(CreatureObject::remove, "CreatureObject::remove");
}
//-----------------------------------------------------------------------
void CreatureObject::remove()
{
}
//-----------------------------------------------------------------------
/**
* Returns a shared template if none was given for this object.
*
* @return the shared template
*/
const SharedObjectTemplate * CreatureObject::getDefaultSharedTemplate() const
{
static const ConstCharCrcLowerString templateName("object/creature/base/shared_creature_default.iff");
if (m_defaultSharedTemplate == NULL)
{
m_defaultSharedTemplate = safe_cast<const SharedObjectTemplate *>(
ObjectTemplateList::fetch(templateName));
WARNING_STRICT_FATAL(m_defaultSharedTemplate == NULL, ("Cannot create "
"default shared object template %s", templateName.getString()));
if (m_defaultSharedTemplate != NULL)
ExitChain::add (removeDefaultTemplate, "CreatureObject::removeDefaultTemplate");
}
return m_defaultSharedTemplate;
} // CreatureObject::getDefaultSharedTemplate
//-----------------------------------------------------------------------
/**
* Cleans up the default shared template.
*/
void CreatureObject::removeDefaultTemplate()
{
if (m_defaultSharedTemplate != NULL)
{
m_defaultSharedTemplate->releaseReference();
m_defaultSharedTemplate = NULL;
}
} // CreatureObject::removeDefaultTemplate
//-----------------------------------------------------------------------
bool CreatureObject::isSpawningAllowed() const
{
ServerObject const * container = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
return isAuthoritative()
&& getScriptObject()->isScriptListInitialized()
&&
(
!container || container->asCreatureObject()
)
&& ( ConfigServerGame::getBoundarySpawningAllowed()
|| getExposedProxyList().empty())
&& ( ConfigServerGame::getEnableSpawningNearLoadBeacons()
|| !ServerWorld::isInLoadBeaconRange(getPosition_w()));
}
//-----------------------------------------------------------------------
void CreatureObject::addToSpawnQueue()
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::addToSpawnQueue");
if (m_addedToSpawnQueue || !isSpawningAllowed())
return;
DEBUG_FATAL(std::find(s_spawnQueue.begin(), s_spawnQueue.end(), getNetworkId()) != s_spawnQueue.end(), ("spawn queue is inconsistant with m_addedToSpawnQueue flag"));
s_spawnQueue.push_back(getNetworkId());
m_addedToSpawnQueue = true;
}
//-----------------------------------------------------------------------
void CreatureObject::removeFromSpawnQueue()
{
if (m_addedToSpawnQueue)
{
std::deque<NetworkId>::iterator f = std::find(s_spawnQueue.begin(), s_spawnQueue.end(), getNetworkId());
if (f != s_spawnQueue.end())
s_spawnQueue.erase(f);
m_addedToSpawnQueue = false;
}
}
//-----------------------------------------------------------------------
void CreatureObject::runSpawnQueue()
{
static int framesPerSpawn = ConfigServerGame::getFramesPerSpawn();
static int64 lastSpawnFrame = 0;
static int64 currentSpawnFrame = 0;
++currentSpawnFrame;
if (currentSpawnFrame - lastSpawnFrame >= framesPerSpawn)
{
int i = 1;
if (framesPerSpawn == 0)
i = ConfigServerGame::getSpawnQueueSize();
while (i > 0 && !s_spawnQueue.empty())
{
NetworkId const id = s_spawnQueue.front();
s_spawnQueue.pop_front();
// Only spawn if we can resolve the object, and it believes spawning
// should be allowed. If we can resolve the object but spawning is not
// allowed, do not re-add it to the queue and mark it as such.
CreatureObject * const spawner = safe_cast<CreatureObject *>(NetworkIdManager::getObjectById(id));
if (spawner)
{
if (spawner->isSpawningAllowed())
{
ScriptParams params;
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(id, "spawn_Trigger", dictionary->getSerializedData(), 0, false);
}
s_spawnQueue.push_back(id);
}
else
spawner->m_addedToSpawnQueue = false;
}
--i;
}
lastSpawnFrame = currentSpawnFrame;
}
}
//-----------------------------------------------------------------------
void CreatureObject::showSpawnQueue(Unicode::String & result)
{
for (std::deque<NetworkId>::iterator i = s_spawnQueue.begin(); i != s_spawnQueue.end(); ++i)
{
ServerObject const * const obj = safe_cast<ServerObject const *>(NetworkIdManager::getObjectById((*i)));
if (obj)
{
result += Unicode::narrowToWide((*i).getValueString());
result += Unicode::narrowToWide(" - ");
result += obj->getObjectName();
result += Unicode::narrowToWide("\n");
}
}
}
//-----------------------------------------------------------------------
/**
*/
bool CreatureObject::assignMission(MissionObject * missionObject)
{
bool result = false;
if(missionObject)
{
ServerObject * datapad = getDatapad();
if(datapad)
{
// if this mission is coming from our own mission bag, increment missions wanted
ServerObject * missionBag = getMissionBag();
if(missionBag)
{
Container * container = ContainerInterface::getContainer(*missionBag);
ContainerIterator iter;
bool found = false;
int i = 0;
for(iter = container->begin(); iter != container->end(); ++iter)
{
MissionObject * mo = safe_cast<MissionObject *>((*iter).getObject());
if(mo)
{
i++;
if(mo == missionObject)
{
found = true;
}
}
}
if(found)
{
setNumberOfMissionsWantedInMissionBag(i + 1);
}
}
Container::ContainerErrorCode tmp = Container::CEC_Success;
result = ContainerInterface::transferItemToGeneralContainer(*datapad, *missionObject, NULL, tmp);
if(result)
{
missionObject->setMissionHolderId(getNetworkId());
}
}
}
return result;
}
//-----------------------------------------------------------------------
void CreatureObject::setMissionBoardUISequenceId(const uint8 sequenceId)
{
m_missionListRequestSequenceId = sequenceId;
}
//-----------------------------------------------------------------------
std::vector<CachedNetworkId> CreatureObject::getMissions()
{
std::vector<CachedNetworkId> result;
ServerObject * const datapad = getDatapad();
if (datapad)
{
Container * const container = ContainerInterface::getContainer(*datapad);
if (container)
{
for (ContainerIterator iter = container->begin(); iter != container->end(); ++iter)
{
ServerObject * const serverMissionObject = safe_cast<ServerObject *>((*iter).getObject());
if (serverMissionObject)
{
MissionObject * const missionObject = serverMissionObject->asMissionObject();
if (missionObject)
result.push_back(*iter);
}
}
}
}
return result;
}
//-----------------------------------------------------------------------
bool CreatureObject::canManipulateObject(ServerObject const &target, bool movingObject, bool doPermissionCheckOnItem, bool doPermissionCheckOnParents, float maxRange, Container::ContainerErrorCode& code, bool skipNoTradeCheck, bool * allowedByGodMode) const
{
if (allowedByGodMode)
*allowedByGodMode = false;
if (getClient() && !ObserveTracker::isObserving(*getClient(), target) && (target.getCacheVersion() == 0))
{
code = Container::CEC_Unknown;
return false;
}
code = Container::CEC_Success;
ServerObjectTemplate const * const targetTemplate = safe_cast<ServerObjectTemplate const *>(target.getObjectTemplate());
NOT_NULL(targetTemplate);
bool canMoveInWorld = false;
bool godCanMove = false;
ServerObject const * const serverTarget = safe_cast<ServerObject const *>(&target);
if (!serverTarget || (movingObject && serverTarget->getCacheVersion() > 0))
{
code = Container::CEC_Unmovable;
return false;
}
if (serverTarget->isInBazaarOrVendor() && !(serverTarget->isVendor() || serverTarget->isBazaarTerminal()))
{
code = Container::CEC_Unknown;
return false;
}
ServerObject const * const containedBy = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*serverTarget));
//-- don't transfer from/to factory crates
if (containedBy && containedBy->getGameObjectType () == SharedObjectTemplate::GOT_misc_factory_crate)
{
code = Container::CEC_Unknown;
return false;
}
{
for (size_t i = 0; i < targetTemplate->getMoveFlagsCount(); ++i)
{
if (targetTemplate->getMoveFlags(i) == ServerObjectTemplate::MF_player)
{
canMoveInWorld = true;
godCanMove = true;
}
else if (targetTemplate->getMoveFlags(i) == ServerObjectTemplate::MF_gm)
godCanMove = true;
}
}
//Gods don't have distance checks or anything, so we can exit early if they are a god at this point.
if ((canMoveInWorld || godCanMove) && getClient() && getClient()->isGod())
{
if (allowedByGodMode)
*allowedByGodMode = true;
return true;
}
//----------------------------------------------------------------------
// see if the object is flagged as being owner-only manipulatable
if (!skipNoTradeCheck)
{
if (target.markedNoTrade())
{
// the item must be owned by this creature
NetworkId ownerId;
if (target.asIntangibleObject())
ownerId = containedBy ? containedBy->getOwnerId() : NetworkId::cms_invalid;
else
ownerId = target.getOwnerId();
if (ownerId != getNetworkId())
{
code = Container::CEC_NoPermission;
return false;
}
}
}
//----------------------------------------------------------------------
// exclude these object types from being moved by players
const int got = serverTarget->getGameObjectType ();
CreatureObject const * const creatureTarget = serverTarget->asCreatureObject();
if (movingObject)
{
// If the object isn't contained they may be trying to move the object
// check the flags on the template to see if it can be picked up.
if (!canMoveInWorld && target.isInWorld ())
{
// DEBUG_REPORT_LOG(true, ("canManipulate failed - This object is in the world and is flagged as immobile\n"));
code = Container::CEC_NoPermission;
return false;
}
// can't move creatures we don't own
if (creatureTarget && creatureTarget->getOwnerId() != getNetworkId())
{
code = Container::CEC_NoPermission;
return false;
}
if (GameObjectTypes::isTypeOf (got, SharedObjectTemplate::GOT_building) ||
GameObjectTypes::isTypeOf (got, SharedObjectTemplate::GOT_installation) ||
got == SharedObjectTemplate::GOT_lair ||
got == SharedObjectTemplate::GOT_camp ||
got == SharedObjectTemplate::GOT_vehicle ||
got == SharedObjectTemplate::GOT_corpse ||
serverTarget->asCellObject() ||
got == SharedObjectTemplate::GOT_data_mission_object)
{
code = Container::CEC_UnmovableType;
return false;
}
//-- can't move items in the world cell unless we are explicitly owning them
if (serverTarget->isInWorldCell () && serverTarget->getOwnerId () != getNetworkId ())
{
code = Container::CEC_NoPermission;
return false;
}
}
//----------------------------------------------------------------------
ServerObject const * const firstParentInWorld = safe_cast<ServerObject const *>(ContainerInterface::getFirstParentInWorld(target));
if (firstParentInWorld && firstParentInWorld != ContainerInterface::getFirstParentInWorld(*this) && firstParentInWorld->asTangibleObject())
{
if (maxRange > 0.0f)
{
float distance = findPosition_w().magnitudeBetween(firstParentInWorld->findPosition_w());
distance -= getRadius();
distance -= firstParentInWorld->getRadius();
// Check range on the server with a 1m fudge factor in it.
if (distance > maxRange + 1.0f)
{
code = Container::CEC_OutOfRange;
return false;
}
}
// skip LOS check if the item is being restored as part of decoration layout restore
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (!playerObject || (playerObject->getRestoreDecorationObjectBeingRestored() != target.getNetworkId()))
{
if (!checkLOSTo(*firstParentInWorld))
{
code = Container::CEC_CantSee;
// DEBUG_REPORT_LOG(true, ("LOS check failed in can manipulate\n"));
return false;
}
}
}
//----------------------------------------------------------------------
bool manipulateOk = true;
if (target.asTangibleObject() && target.asTangibleObject()->isLocked())
{
TangibleObject const * tangibleTarget = target.asTangibleObject();
bool allow = false;
if(tangibleTarget->isUserOnAccessList(getNetworkId()) || tangibleTarget->isGuildOnAccessList(getGuildId()))
{
// Still have to obey house rules.
ServerObject const * const topmost = safe_cast<ServerObject const *>(ContainerInterface::getTopmostContainer(*serverTarget));
if (movingObject && topmost)
{
//Handle the case where we are in a designer placed building and a designer placed container (loot chest)
if ( topmost->getOwnerId() == NetworkId::cms_invalid
&& ( firstParentInWorld->getOwnerId() == NetworkId::cms_invalid
|| firstParentInWorld->getOwnerId() == getNetworkId())
&& serverTarget != firstParentInWorld)
{
allow = true;
}
//Handle the case where we are in a player building or pob ship
else if (topmost->getOwnerId() == getNetworkId() || isBuildingAdminForTarget (*this, *topmost))
{
const bool isContainerPortallized = topmost->getPortalProperty() != 0;
allow = (!isContainerPortallized || !movingObject || !isTargetBuildingBaseObject (*topmost, *serverTarget));
}
else
allow = false;
}
else
allow = true;
}
else
{
// Our target is locked, but allow admins on a building to be able to manipulate the object.
ServerObject const * const topmost = safe_cast<ServerObject const *>(ContainerInterface::getTopmostContainer(*serverTarget));
if (topmost)
{
//Handle the case where we are in a designer placed building and a designer placed container (loot chest)
if ( topmost->getOwnerId() == NetworkId::cms_invalid
&& ( firstParentInWorld->getOwnerId() == NetworkId::cms_invalid
|| firstParentInWorld->getOwnerId() == getNetworkId())
&& serverTarget != firstParentInWorld)
{
allow = true;
}
//Handle the case where we are in a player building or pob ship
else if (topmost->getOwnerId() == getNetworkId())
{
const bool isContainerPortallized = topmost->getPortalProperty() != 0;
allow = (!isContainerPortallized || !movingObject || !isTargetBuildingBaseObject (*topmost, *serverTarget));
}
else
allow = false;
}
}
if(!allow)
code = Container::CEC_NoPermission;
return allow;
}
//----------------------------------------------------------------------
//-- see if we need to check permissions of the object itself
if (doPermissionCheckOnItem)
{
//Do not allow players to manipulate other players or things contained by other players
if (serverTarget->isPlayerControlled() && serverTarget != this)
{
code = Container::CEC_NoPermission;
return false;
}
if (serverTarget->getOwnerId () == getNetworkId())
{
manipulateOk = true;
}
//-- an invalid owner means we can manipulate the item in general,
//-- e.g. opening a container, but not move it
else if (movingObject || serverTarget->getOwnerId ().isValid ())
{
manipulateOk = false;
}
}
//----------------------------------------------------------------------
//-- see if the permission check on parents needs to be carried out.
//ParentCheck on item
//if item is a vendor, get topmost and check vendor list
//else
// get first parent in world
// if is an installation check hopper list
// else if it is a creature, check the immediate parent for ownership only (covers corpses, inventory, etc)
// else check ownership of topmost, with 0 being allowed.
// if topmost happens to be a player building, check its admin list and make sure the object being moved isn't part of the house
// else if topmost happens to be an unowned building, then any sub containers can be looted
if (doPermissionCheckOnParents)
{
// Check our access list.
if(containedBy && containedBy->asTangibleObject() && containedBy->asTangibleObject()->isLocked())
{
TangibleObject const * tangibleParent = containedBy->asTangibleObject();
if(tangibleParent->isUserOnAccessList(getNetworkId()) || tangibleParent->isGuildOnAccessList(getGuildId()))
{
manipulateOk = true;
}
else
{
manipulateOk = false;
code = Container::CEC_NoPermission;
}
}
else if (!manipulateOk || movingObject)
{
//-- can't move items contained by a manufacturing schematic
{
if (containedBy && containedBy->getGameObjectType () == SharedObjectTemplate::GOT_data_manufacturing_schematic)
{
code = Container::CEC_NoPermission;
return false;
}
}
if (firstParentInWorld)
{
const int containerGot = firstParentInWorld->getGameObjectType ();
if (GameObjectTypes::isTypeOf (containerGot, SharedObjectTemplate::GOT_installation))
{
//-- test admin status on installation
manipulateOk = canHopperForTarget (*this, *firstParentInWorld);
}
else if (firstParentInWorld->asCreatureObject())
{
// check for ownership
manipulateOk = (containedBy && (containedBy->getOwnerId() == getNetworkId() || containedBy->getOwnerId() == NetworkId::cms_invalid));
}
else
{
ServerObject const * const topmost = safe_cast<ServerObject const *>(ContainerInterface::getTopmostContainer(*serverTarget));
if (topmost)
{
//Handle the case where we are in a designer placed building and a designer placed container (loot chest)
if ( topmost->getOwnerId() == NetworkId::cms_invalid
&& ( firstParentInWorld->getOwnerId() == NetworkId::cms_invalid
|| firstParentInWorld->getOwnerId() == getNetworkId())
&& serverTarget != firstParentInWorld)
{
manipulateOk = true;
}
//Handle the case where we are in a player building or pob ship
else if (topmost->getOwnerId() == getNetworkId() || isBuildingAdminForTarget (*this, *topmost))
{
const bool isContainerPortallized = topmost->getPortalProperty() != 0;
manipulateOk = (!isContainerPortallized || !movingObject || !isTargetBuildingBaseObject (*topmost, *serverTarget));
}
else
manipulateOk = false;
}
else
manipulateOk = false;
}
}
else
manipulateOk = false;
}
}
//----------------------------------------------------------------------
if (!manipulateOk && code == Container::CEC_Success)
code = Container::CEC_NoPermission;
return manipulateOk;
}
//-----------------------------------------------------------------------
/**
* Do any processing needed during endBaselines() to handle this object if
* it is contained by another object.
*
* @return true if object should have addToWorld() called on it; false otherwise.
*/
bool CreatureObject::handleContentsSetup()
{
bool ret = TangibleObject::handleContentsSetup();
setStatesDueToContainment();
return ret;
}
// ----------------------------------------------------------------------
void CreatureObject::setStatesDueToContainment()
{
uint64 oldStates = m_states.get();
uint64 states = oldStates&(~s_containmentStatesMask);
ServerObject const *containingObject = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
if (containingObject)
{
SlottedContainer const *slottedContainer = containingObject->getSlottedContainerProperty();
if (slottedContainer)
{
SlottedContainmentProperty const *slottedContainmentProperty = ContainerInterface::getSlottedContainmentProperty(*this);
if (slottedContainmentProperty)
{
int currentArrangement = slottedContainmentProperty->getCurrentArrangement();
if (currentArrangement != -1)
{
SlottedContainmentProperty::SlotArrangement const &slotArrangement = slottedContainmentProperty->getSlotArrangement(currentArrangement);
for (SlottedContainmentProperty::SlotArrangement::const_iterator i = slotArrangement.begin(); i != slotArrangement.end(); ++i)
states |= s_containerSlotStates[*i];
}
}
}
else if (containingObject->asCellObject())
{
// special case - state set when in the interior of a pob ship
ServerObject const * const portallizedObject = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*containingObject));
if (portallizedObject && portallizedObject->asShipObject())
states |= States::getStateMask(States::ShipInterior);
}
}
if (states != oldStates)
m_states = states;
}
//-----------------------------------------------------------------------
void CreatureObject::forwardServerObjectSpecificBaselines() const
{
TangibleObject::forwardServerObjectSpecificBaselines();
Property const * const property = getProperty(SlowDownProperty::getClassPropertyId());
if (property != NULL)
{
SlowDownProperty const * const slowdownProperty = safe_cast<const SlowDownProperty *>(property);
SlowDownEffectMessage const message(getNetworkId(), slowdownProperty->getTarget(), slowdownProperty->getConeLength(), slowdownProperty->getConeAngle(), slowdownProperty->getSlopeAngle(), slowdownProperty->getExpireTime());
ServerMessageForwarding::send(message);
}
// if we are an ai creature, have our controller send our current ai state
AICreatureController const * const aiCreatureController = AICreatureController::asAiCreatureController(getController());
if (aiCreatureController != NULL)
{
aiCreatureController->forwardServerObjectSpecificBaselines();
}
}
//-----------------------------------------------------------------------
void CreatureObject::sendObjectSpecificBaselinesToClient(Client const &client) const
{
TangibleObject::sendObjectSpecificBaselinesToClient(client);
Property const * const property = getProperty(SlowDownProperty::getClassPropertyId());
if (property != NULL)
{
SlowDownProperty const * const slowdownProperty = safe_cast<const SlowDownProperty *>(property);
SlowDownEffectMessage const message(getNetworkId(), slowdownProperty->getTarget(), slowdownProperty->getConeLength(), slowdownProperty->getConeAngle(), slowdownProperty->getSlopeAngle(), slowdownProperty->getExpireTime());
client.send(message, true);
}
UpdatePostureMessage const updatePostureMessage(getNetworkId(), getPosture());
client.send(updatePostureMessage, true);
}
//-----------------------------------------------------------------------
/**
* Initializes a newly created object.
*/
void CreatureObject::initializeFirstTimeObject()
{
TangibleObject::initializeFirstTimeObject();
//-- Get ourselves into a reasonable locomotion state.
updateMovementInfo();
// create the default weapon
initializeDefaultWeapon();
// set the current weapon
WeaponObject * weapon = getReadiedWeapon();
if (weapon != NULL)
setCurrentWeapon(*weapon);
//Quick error check since all creatures should have a default weapon
WARNING_STRICT_FATAL(!getDefaultWeapon(), ("Creature %s (%s) has no default "
"weapon", getNetworkId().getValueString().c_str(), getTemplateName()));
const ServerCreatureObjectTemplate * myTemplate = safe_cast<const ServerCreatureObjectTemplate *>(getObjectTemplate());
Unicode::String newName = NameManager::getInstance().generateRandomName(ConfigServerGame::getCharacterNameGeneratorDirectory(), myTemplate->getNameGeneratorType());
if (!newName.empty())
{
setObjectName(newName);
}
setOwnerId(getNetworkId());
recomputeSlopeModPercent();
setupSkillData();
packWearables();
ServerObject *bankContainer = getBankContainer();
if (bankContainer)
bankContainer->setLoadContents(false);
// note: this MUST come after TangibleObject::initializeFirstTimeObject() is called
float defaultAlterTime = 0;
if (!isPlayerControlled())
defaultAlterTime = AlterResult::cms_alterQuickly;
// DEBUG_REPORT_LOG(true, ("[aitest] setting creature %s default alter time to %f\n", getNetworkId().getValueString().c_str(), defaultAlterTime));
setDefaultAlterTime(defaultAlterTime);
} // CreatureObject::initializeFirstTimeObject
//-----------------------------------------------------------------------
void CreatureObject::onLoadedFromDatabase()
{
typedef const std::string * strptr;
if (isAuthoritative())
{
if (isPlayerControlled())
{
onClientAboutToLoad();
// Initialize A-Tab info if we need to.
SlottedContainer * const container = ContainerInterface::getSlottedContainer(*this);
if(container)
{
SlotId slot;
Container::ContainerErrorCode tmp;
slot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::appearance);
if(slot != SlotId::invalid)
{
Container::ContainedItem itemId = container->getObjectInSlot(slot, tmp);
Object* appearanceInventory = itemId.getObject();
if(appearanceInventory == NULL)
{
WARNING(true, ("Player %s has lost their appearance inventory", getNetworkId().getValueString().c_str()));
appearanceInventory = ServerWorld::createNewObject(s_appearanceTemplate, *this, slot, false);
if(!appearanceInventory)
{
DEBUG_FATAL(true, ("Could not create an appearance inventory for the player who lost theirs"));
}
}
}
}
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
// move old badges over to the collection system
if (getObjVars().hasItem(OBJVAR_BADGES) && (getObjVars().getType(OBJVAR_BADGES) == DynamicVariable::INT_ARRAY))
{
std::vector<int> badges;
getObjVars().getItem(OBJVAR_BADGES, badges);
if (!badges.empty())
playerObject->migrateLegacyBadgesToCollection(badges);
removeObjVarItem(OBJVAR_BADGES);
removeObjVarItem(OBJVAR_COUNT_BADGES);
}
// check for Jedi initialization
if (getObjVars().hasItem(OBJVAR_NEW_JEDI_FORCE))
{
int start_force;
getObjVars().getItem(OBJVAR_NEW_JEDI_FORCE, start_force);
removeObjVarItem(OBJVAR_NEW_JEDI_FORCE);
if (start_force > 0)
{
playerObject->setMaxForcePower(start_force);
playerObject->setForcePowerRegenRate(ConfigServerGame::getMinForcePowerRegenRate());
}
else
{
WARNING(true, ("Player %s had the %s objvar, but it had a "
"value of %d", getNetworkId().getValueString().c_str(),
OBJVAR_NEW_JEDI_FORCE.c_str(), start_force));
}
}
// check the non-pool attributes: their value should be the
// same as their max value, since we don't regenerate/heal them
for (int i = 0; i < Attributes::NumberOfAttributes; ++i)
{
if (Attributes::isAttribPool(i))
continue;
Attributes::Value attrib = getUnmodifiedAttribute(i);
Attributes::Value max = getUnmodifiedMaxAttribute(i);
if (attrib != max)
setAttribute(i, max);
}
// initialize the character born date, if necessary
playerObject->setBornDate();
// retroactively set CTS history and CTS title(s), if necessary,
// and fix up the character's born date, if necessary, if it was
// incorrectly changed from a prior CTS where the character's born
// date was not transferred;
//
// should be done *AFTER* the above statement to initialize the
// character born date, if necessary
GameServer::setRetroactiveCtsHistory(*this);
// set the "born on <galaxy>" title if this galaxy supports
// it and it hasn't already been set on this character
if (!getObjVars().hasItem("bornOnGalaxyTitleSet"))
{
// lower case cluster name and replace - with _ for use with the collection slot name
std::string collectionSlotName = std::string("born_on_") + Unicode::toLower(GameServer::getInstance().getClusterName());
std::string::size_type pos = collectionSlotName.find('-');
while (pos != std::string::npos)
{
collectionSlotName.replace(pos, 1, 1, '_');
pos = collectionSlotName.find('-');
}
CollectionsDataTable::CollectionInfoSlot const * collectionSlot = CollectionsDataTable::getSlotByName(collectionSlotName);
if (collectionSlot)
{
FATAL((collectionSlot->collection.name != std::string("born_on_collection")), ("collection slot %s must be in collection born_on_collection", collectionSlot->name.c_str()));
FATAL((collectionSlot->collection.page.name != std::string("born_on_page")), ("collection slot %s must be in collection page born_on_page", collectionSlot->name.c_str()));
FATAL((collectionSlot->collection.page.book.name != std::string("born_on_book")), ("collection slot %s must be in collection book born_on_book", collectionSlot->name.c_str()));
// see if character transferred from another galaxy, and
// if yes, then the original galaxy is the home galaxy
std::string originalSourceGalaxy;
int earliestTransferTime = -1;
DynamicVariableList::NestedList const ctsTransactions(getObjVars(), "ctsHistory");
for (DynamicVariableList::NestedList::const_iterator i = ctsTransactions.begin(); i != ctsTransactions.end(); ++i)
{
Unicode::String ctsTransactionDetail;
if (i.getValue(ctsTransactionDetail))
{
Unicode::UnicodeStringVector tokens;
if (Unicode::tokenize(ctsTransactionDetail, tokens, NULL, NULL) && (tokens.size() >= 4))
{
int const transferTime = atoi(Unicode::wideToNarrow(tokens[0]).c_str());
if ((earliestTransferTime == -1) || (transferTime < earliestTransferTime))
{
earliestTransferTime = transferTime;
originalSourceGalaxy = Unicode::wideToNarrow(tokens[1]);
}
}
}
}
// character transferred from another galaxy, so
// use the original galaxy as the home galaxy
if (!originalSourceGalaxy.empty())
{
// lower case cluster name and replace - with _ for use with the collection slot name
collectionSlotName = std::string("born_on_") + Unicode::toLower(originalSourceGalaxy);
std::string::size_type pos = collectionSlotName.find('-');
while (pos != std::string::npos)
{
collectionSlotName.replace(pos, 1, 1, '_');
pos = collectionSlotName.find('-');
}
collectionSlot = CollectionsDataTable::getSlotByName(collectionSlotName);
if (collectionSlot)
{
FATAL((collectionSlot->collection.name != std::string("born_on_collection")), ("collection slot %s must be in collection born_on_collection", collectionSlot->name.c_str()));
FATAL((collectionSlot->collection.page.name != std::string("born_on_page")), ("collection slot %s must be in collection page born_on_page", collectionSlot->name.c_str()));
FATAL((collectionSlot->collection.page.book.name != std::string("born_on_book")), ("collection slot %s must be in collection book born_on_book", collectionSlot->name.c_str()));
}
}
}
// set "born on <galaxy>" collection slot and clear all the other "born on <galaxy>" collection slots;
// this still needs to be run even if collectionSlot is NULL in order to forcefully clear all of the
// other "born on <galaxy>" collection slots, since we are reusing deleted/no longer used collection
// slot bits, and those bits may be left in a set state at the time they were deleted/no longer used
std::vector<CollectionsDataTable::CollectionInfoSlot const *> const & slots = CollectionsDataTable::getSlotsInCollection("born_on_collection");
for (std::vector<CollectionsDataTable::CollectionInfoSlot const *>::const_iterator iter = slots.begin(); iter != slots.end(); ++iter)
{
playerObject->modifyCollectionSlotValue((*iter)->name, ((*iter == collectionSlot) ? 1ll : -1ll));
}
IGNORE_RETURN(setObjVarItem("bornOnGalaxyTitleSet", 1));
}
}
// wait for all loading/initialization to complete, and then double
// check the character's title to make sure it is a valid title
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++CheckCharacterTitle", "", 30, false);
}
// store the default regen values in the former faucet attrib slots
m_attributes.set(Attributes::Constitution, static_cast<int>(ConfigServerGame::getDefaultHealthRegen()));
m_attributes.set(Attributes::Stamina, static_cast<int>(ConfigServerGame::getDefaultActionRegen()));
m_attributes.set(Attributes::Willpower, static_cast<int>(ConfigServerGame::getDefaultMindRegen()));
if(!isPlayerControlled())
fixupPersistentBuffsAfterLoading();
else
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject && playerObject->isInitialized())
{
fixupPersistentBuffsAfterLoading();
fixupLevelXpAfterLoading();
}
}
// there are cetain states we don't want to be in on start-up
setState(States::Cover, false);
setState(States::Combat, false);
setState(States::Peace, false);
setState(States::Aiming, false);
setState(States::Meditate, false);
setState(States::Berserk, false);
setState(States::FeignDeath, false);
setState(States::Tumbling, false);
setState(States::Rallied, false);
setState(States::Stunned, false);
setState(States::Blinded, false);
setState(States::Dizzy, false);
setState(States::Intimidated, false);
setState(States::Immobilized, false);
setState(States::Crafting, false);
setState(States::MountedCreature, false);
setState(States::Disguised, false);
setStatesDueToContainment();
// override the speeds stored in the database
// @todo: make the speeds non-persisted
setBaseWalkSpeed((safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_walk)));
setBaseRunSpeed((safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_run)));
}
// setupSkillData MUST be called before we add the bonuses for equipment!
setupSkillData();
packWearables();
if (isAuthoritative() && isPlayerControlled())
{
// make sure the player has no more expertise skills than they should have for their level
// if they do, log them as a suspected cheater
int const remainingExpertisePoints = getRemainingExpertisePoints();
if (remainingExpertisePoints < 0)
{
LOG("CustomerService",
("SuspectedCheaterChannel: %s has more expertises than level %d allows. Amount over limit is %d.",
PlayerObject::getAccountDescription(this).c_str(), getLevel(), -remainingExpertisePoints)
);
}
// if we are a player, go through all the creature's equipment and update for
// bonus skill mods
std::vector<std::pair<std::string, int> > skillModBonuses;
std::vector<std::pair<int, int> > attribBonuses;
Container const * const equipment = ContainerInterface::getContainer(*this);
if (equipment != NULL)
{
for (ContainerConstIterator i(equipment->begin()); i != equipment->end(); ++i)
{
ServerObject const * const so = safe_cast<ServerObject const *>((*i).getObject());
if (so)
{
TangibleObject const * const equippedItem = so->asTangibleObject();
if (equippedItem != NULL)
{
equippedItem->getSkillModBonuses(skillModBonuses);
int bonusCount = skillModBonuses.size();
{
for (int j = 0; j < bonusCount; ++j)
{
std::pair<std::string, int> const &bonus = skillModBonuses[j];
addModBonus(bonus.first, bonus.second);
}
}
equippedItem->getAttribBonuses(attribBonuses);
bonusCount = attribBonuses.size();
{
for (int j = 0; j < bonusCount; ++j)
{
std::pair<int, int> const &bonus = attribBonuses[j];
addAttribBonus(bonus.first, bonus.second);
}
}
}
}
}
}
}
TangibleObject::onLoadedFromDatabase();
if (isAuthoritative())
{
// note: this MUST come after TangibleObject::onLoadedFromDatabase() is called
float defaultAlterTime = 0;
if (!isPlayerControlled())
defaultAlterTime = AlterResult::cms_alterQuickly;
// DEBUG_REPORT_LOG(true, ("[aitest] setting creature %s default alter time to %f\n", getNetworkId().getValueString().c_str(), defaultAlterTime));
setDefaultAlterTime(defaultAlterTime);
CreatureController * controller = getCreatureController();
if (controller != NULL)
controller->updateHibernate();
}
}
//-----------------------------------------------------------------------
/**
* Initializes a proxy object.
*/
void CreatureObject::endBaselines()
{
// catch people in potentially deleted newbie halls
if (NewbieTutorial::getSceneId() == ServerWorld::getSceneId())
getContainedByProperty()->setContainedBy(NetworkId::cms_invalid);
TangibleObject::endBaselines();
if (isAuthoritative())
{
// set our initial posture depending if we are incapacitated/dead or not
Postures::Enumerator posture = getPosture();
if (isDead())
posture = Postures::Dead;
if (getAttribute(Attributes::Health) <= 0)
{
// make sure we are incapacitated, unless we are dead
if (posture != Postures::Dead)
posture = Postures::Incapacitated;
}
if (posture != getPosture())
setPosture(posture);
updateMovementInfo();
// Fix up guildId - set regardless of whether it is already correct, as this
// triggers entry into the guild chat room.
int guildId = GuildInterface::getGuildId(getNetworkId());
// fix up issue there a guild leader gets mistakenly removed
// from the guild (i.e. the guild leader's permission is reset
// to 0, meaning he is only a sponsored member, and the guild
// id won't be returned in GuildInterface::getGuildId()), causing
// the guild to be leaderless; we only need to get the guild id
// for the guild that this character is the leader of; setGuildId()
// will do the rest of the work
if (guildId == 0)
guildId = GuildInterface::getGuildIdForGuildLeader(getNetworkId());
setGuildId(guildId);
// Fix up guildWarEnabled
setGuildWarEnabled(GuildInterface::getGuildMemberGuildWarEnabled(getGuildId(), getNetworkId()));
// Fix up militiaOfCityId
setMilitiaOfCityId(CityInterface::getMilitiaOfCityId(getNetworkId()));
// Mount: needs to grab its position from the rider since the rider dictates
// mount position.
if (getState(States::MountedCreature))
{
CreatureObject const *const rider = getPrimaryMountingRider();
if (rider)
{
// Use parent space since both the rider and mount should be
// attached to the same thing. Initially this is limited to
// the world.
setPosition_p(rider->getPosition_p());
}
}
// depersist persisted command cooldowns
CommandQueue * commandQueue = getCommandQueue();
if (commandQueue)
{
commandQueue->depersistCooldown();
}
// make sure that factional player characters are at least rank 1 (rating 0)
// and non-factional player characters have no current GCW points or rating
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player)
{
static int const min = std::numeric_limits<int>::min();
if (PvpData::isImperialFactionId(getPvpFaction()) || PvpData::isRebelFactionId(getPvpFaction()))
{
// decrementing the value by a really large
// number will cause it to reset to 0
if (player->getCurrentGcwRating() < Pvp::getMinRatingForRank())
player->modifyCurrentGcwRating(min, true);
}
else
{
// decrementing the value by a really large
// number will cause it to reset to 0
if (player->getCurrentGcwPoints() > 0)
player->modifyCurrentGcwPoints(min, false);
if (player->getCurrentGcwRating() != -1)
player->modifyCurrentGcwRating(min, false);
if (player->getCurrentPvpKills() > 0)
player->modifyCurrentPvpKills(min, false);
}
// make sure that m_nextGcwRatingCalcTime is set so that the
// rating will be recalculated at the next recalculation interval
player->setNextGcwRatingCalcTime(true);
// migrate veteran rewards claim tracking information to new system
VeteranRewardManager::migrateVeteranRewardsClaimInformation(*this);
// request the list of account feature transactions for the character
// from the LoginServer DB which will be compared to the list of account
// feature transactions stored on the character, and if there is any
// discrepancy, then it probably means there was a rollback, so we would
// need to grant the character any missing item(s)
FeatureIdTransactionRequest const fitr(GameServer::getInstance().getProcessId(), player->getStationId(), getNetworkId());
GameServer::getInstance().sendToCentralServer(fitr);
// set the guild rank
GuildMemberInfo const * const guildMemberInfo = GuildInterface::getGuildMemberInfo(getGuildId(), getNetworkId());
if (guildMemberInfo)
player->setGuildRank(guildMemberInfo->m_rank);
// set the citizen rank
// join the city chat room
std::vector<int> const & cityIds = CityInterface::getCitizenOfCityId(getNetworkId());
if (!cityIds.empty())
{
int const cityId = cityIds.front();
CitizenInfo const * const citizenInfo = CityInterface::getCitizenInfo(cityId, getNetworkId());
if (citizenInfo)
player->setCitizenRank(citizenInfo->m_citizenRank);
// join the city chat room
CityInterface::enterCityChatRoom(cityId, *this);
}
// set the player's citizenship information
player->updateCitizenshipInfo();
// set the player's GCW Region info
player->updateGcwRegionInfo();
// set the player's GCW Region Defender info
player->updateGcwDefenderRegionInfo();
// fix the citizen name in the citizen roster, if necessary
CityInterface::verifyCitizenName(getNetworkId(), Unicode::wideToNarrow(getAssignedObjectName()));
// check for over lot limit condition
player->setAccountNumLotsOverLimitSpam();
// fix the pgc chronicler's name in the pgc rating data, if necessary
CityInterface::verifyPgcChroniclerName(getNetworkId(), Unicode::wideToNarrow(getAssignedObjectName()));
// depersist galactic reserve
{
// migrate any galactic reserve information that's stored in the PlayerObject into the CreatureObject
int temp;
if (player->getObjVars().getItem("galactic_reserve", temp))
{
player->removeObjVarItem("galactic_reserve");
setObjVarItem("galactic_reserve", temp);
}
if (player->getObjVars().getItem("galactic_reserve_cooldown", temp))
{
player->removeObjVarItem("galactic_reserve_cooldown");
setObjVarItem("galactic_reserve_cooldown", temp);
}
player->depersistGalacticReserveDeposit();
}
// depersist mercenary status
if (!ConfigServerGame::getEnableCovertImperialMercenary())
{
player->modifyCollectionSlotValue("covert_imperial_mercenary", -1);
}
if (!ConfigServerGame::getEnableOvertImperialMercenary())
{
player->modifyCollectionSlotValue("overt_imperial_mercenary", -1);
}
if (!ConfigServerGame::getEnableCovertRebelMercenary())
{
player->modifyCollectionSlotValue("covert_rebel_mercenary", -1);
}
if (!ConfigServerGame::getEnableOvertRebelMercenary())
{
player->modifyCollectionSlotValue("overt_rebel_mercenary", -1);
}
if (!PvpData::isNeutralFactionId(getPvpFaction()))
{
player->modifyCollectionSlotValue("covert_imperial_mercenary", -1);
player->modifyCollectionSlotValue("overt_imperial_mercenary", -1);
player->modifyCollectionSlotValue("covert_rebel_mercenary", -1);
player->modifyCollectionSlotValue("overt_rebel_mercenary", -1);
}
else
{
if (player->hasCompletedCollectionSlot("covert_imperial_mercenary"))
{
Pvp::setNeutralMercenaryFaction(*this, PvpData::getImperialFactionId(), PvpType_Covert);
}
else if (player->hasCompletedCollectionSlot("overt_imperial_mercenary"))
{
Pvp::setNeutralMercenaryFaction(*this, PvpData::getImperialFactionId(), PvpType_Declared);
}
else if (player->hasCompletedCollectionSlot("covert_rebel_mercenary"))
{
Pvp::setNeutralMercenaryFaction(*this, PvpData::getRebelFactionId(), PvpType_Covert);
}
else if (player->hasCompletedCollectionSlot("overt_rebel_mercenary"))
{
Pvp::setNeutralMercenaryFaction(*this, PvpData::getRebelFactionId(), PvpType_Declared);
}
}
}
}
else
{
getAvailableLocomotions();
}
checkAndRestoreRequiredSlots();
// get the current weapon, which will set it to our held or default weapon
IGNORE_RETURN(getCurrentWeapon());
// Rider: needs to not participate in CollisionWorld.
if (getState(States::RidingMount))
{
CollisionProperty *const collisionProperty = getCollisionProperty();
if (collisionProperty)
{
if (isInWorld())
CollisionWorld::removeObject(this);
collisionProperty->setDisableCollisionWorldAddRemove(true);
}
}
// Fix up the footprint's swim height to take into consideration any scaling on this creature.
Footprint *const footprint = getFootprint();
if (footprint)
footprint->setSwimHeight(getSwimHeight());
if (isAuthoritative())
recomputeSlopeModPercent();
if (ConfigServerGame::getMoveSimEnabled ())
MoveSimManager::checkApplySimulation (*this);
if (isAuthoritative())
GroupMissionCriticalObjectsBuilder::updateGroupMissionCriticalObjects(*this, true);
} // CreatureObject::endBaselines
//-----------------------------------------------------------------------
void CreatureObject::checkAndRestoreRequiredSlots()
{
SlottedContainer * const container = ContainerInterface::getSlottedContainer(*this);
if (container == NULL)
{
WARNING_STRICT_FATAL(true, ("This creature is not slotted!"));
return;
}
// get the default weapon from it's slot and set the class variable
bool foundDefaultWeapon = false;
Container::ContainerErrorCode tmp = Container::CEC_Success;
Container::ContainedItem const itemId = container->getObjectInSlot(s_defaultWeaponSlotId, tmp);
Object * const o = itemId.getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so && so->asWeaponObject())
foundDefaultWeapon = true;
}
if (!foundDefaultWeapon)
{
if (isAuthoritative())
{
// try to make the default weapon from the template instead
initializeDefaultWeapon();
}
else
{
WARNING_STRICT_FATAL(true, ("Unable to find default weapon slot id for %s(%s)",
getNetworkId().getValueString().c_str(),
getObjectTemplateName()));
}
}
if (isPlayerControlled() && isAuthoritative())
{
//restore inventory, mission bag, and datapad for players
SlotId slot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::inventory);
if (slot != SlotId::invalid)
{
Container::ContainedItem itemId = container->getObjectInSlot(slot, tmp);
Object* inventory = itemId.getObject();
if (inventory == NULL)
{
WARNING(true, ("Player %s has lost their inventory", getNetworkId().getValueString().c_str()));
inventory = ServerWorld::createNewObject(s_inventoryTemplate, *this, slot, false);
if (!inventory)
{
DEBUG_FATAL(true, ("Could not create an inventory for the player who lost theirs"));
}
}
}
slot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::datapad);
if (slot != SlotId::invalid)
{
Container::ContainedItem itemId = container->getObjectInSlot(slot, tmp);
Object* datapad = itemId.getObject();
if (datapad == NULL)
{
WARNING(true, ("Player %s has lost their datapad", getNetworkId().getValueString().c_str()));
datapad = ServerWorld::createNewObject(s_datapadTemplate, *this, slot, false);
if (!datapad)
{
DEBUG_FATAL(true, ("Could not create an datapad for the player who lost theirs"));
}
}
}
slot = SlotIdManager::findSlotId(ConstCharCrcLowerString("mission_bag"));
if (slot != SlotId::invalid)
{
Container::ContainedItem itemId = container->getObjectInSlot(slot, tmp);
ServerObject* missionBag = safe_cast<ServerObject*>(itemId.getObject());
if (missionBag == NULL)
{
WARNING(true, ("Player %s has lost their mission bag", getNetworkId().getValueString().c_str()));
missionBag = ServerWorld::createNewObject(s_missionBagTemplate, *this, slot, false);
if (!missionBag)
{
DEBUG_FATAL(true, ("Could not create a mission bag for the player who lost theirs"));
}
else
missionBag->persist();
}
}
slot = SlotIdManager::findSlotId(ConstCharCrcLowerString("bank"));
if (slot != SlotId::invalid)
{
Container::ContainedItem itemId = container->getObjectInSlot(slot, tmp);
ServerObject* bank = safe_cast<ServerObject*>(itemId.getObject());
if (bank == NULL)
{
WARNING(true, ("Player %s has lost their bank", getNetworkId().getValueString().c_str()));
bank = ServerWorld::createNewObject(s_bankTemplate, *this, slot, false);
if (!bank)
{
DEBUG_FATAL(true, ("Could not create a bank for the player who lost theirs"));
}
else
{
bank->persist();
bank->setLoadContents(false);
}
}
}
}
}
//-----------------------------------------------------------------------
/**
* Allows an object to do pre-deletion cleanup.
*/
void CreatureObject::onRemovingFromWorld()
{
TangibleObject::onRemovingFromWorld();
if (isAuthoritative())
{
// if we are crafting, end the crafting session
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL && player->isCrafting())
player->stopCrafting(false);
// exit all notify regions
Archive::AutoDeltaSet<std::pair<std::string, std::string> >::const_iterator i;
for(i = m_notifyRegions.begin(); i != m_notifyRegions.end(); ++i)
{
ScriptParams params;
params.addParam(i->first.c_str());
params.addParam(i->second.c_str());
if(getScriptObject())
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_EXIT_REGION, params));
}
}
m_notifyRegions.clear();
}
if (isMountable())
IGNORE_RETURN(detachAllRiders());
{
}
}
void CreatureObject::addToWorld()
{
if (getKill())
return;
if (isInWorld())
{
DEBUG_WARNING(true, ("Calling add to world on object already in it %s", getNetworkId().getValueString().c_str()));
return;
}
updateWorldSphere();
Object::addToWorld();
updateTriggerVolumes();
if (getScriptObject() != NULL)
getScriptObject()->setOwnerIsLoaded();
ContainedByProperty * const containedByProperty = getContainedByProperty();
if (containedByProperty && containedByProperty->getContainedByNetworkId().isValid())
{
Object const * const containedByObject = containedByProperty->getContainedBy();
if (containedByObject && !containedByObject->isInWorld())
{
if (!ServerWorld::isSpaceScene())
{
// attempt to put the character at the ejection point of the building
DynamicVariableLocationData ejectionPoint;
if (getObjVars().getItem("building_ejection_point", ejectionPoint) && (ServerWorld::getSceneId() == ejectionPoint.scene) && (!ejectionPoint.cell.isValid()))
{
DEBUG_REPORT_LOG(true,("Ejecting player %s from building %s, because the building does not exist anymore or is not loaded in the world\n",getNetworkId().getValueString().c_str(), containedByProperty->getContainedByNetworkId().getValueString().c_str()));
LOG("CustomerService",("Login: Ejecting player %s from building %s, because the building does not exist anymore or is not loaded in the world",getNetworkId().getValueString().c_str(), containedByProperty->getContainedByNetworkId().getValueString().c_str()));
removeObjVarItem("building_ejection_point");
teleportObject(ejectionPoint.pos, ejectionPoint.cell, "", ejectionPoint.pos,"");
}
else
// warp the player to the Mos Eisley Starport
{
DEBUG_REPORT_LOG(true, ("Tried to load creature %s into invalid container. Moving to Mos Eisley Starport.\n", getNetworkId().getValueString().c_str()));
LOG("CustomerService",("Login: Tried to load creature %s into invalid container. Moving to Mos Eisley Starport.\n", getNetworkId().getValueString().c_str()));
GameServer::getInstance().requestSceneWarp(CachedNetworkId(*this),"tatooine",Vector(3528,5,-4804),NetworkId::cms_invalid,Vector(3528,5,-4804));
}
}
}
}
onAddedToWorld();
}
//-----------------------------------------------------------------------
void CreatureObject::initializeNewPlayer()
{
}
//-----------------------------------------------------------------------
/** Set up the inventory object for the creature and initialize with contents from template.
*/
void CreatureObject::setupInventory()
{
}
//-----------------------------------------------------------------------
/**
* Sets up the creature's skills and related data.
*/
void CreatureObject::setupSkillData()
{
if (isAuthoritative())
{
// clear out our current skill related data
m_modMap.clear();
if (isPlayerControlled())
{
clearCommands();
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
{
playerObject->clearSchematics();
}
}
m_skills.erase(0);
// set the skill related data from the skills
for (SkillList::const_iterator skillIter = m_skills.begin(); skillIter != m_skills.end(); ++skillIter)
{
SkillObject const * skill = *skillIter;
if (skill)
{
// init skillmods
std::vector<std::pair<std::string, int> > const &statMods = skill->getStatisticModifiers();
std::vector<std::pair<std::string, int> >::const_iterator modIter;
for (modIter = statMods.begin(); modIter != statMods.end(); ++modIter)
{
addModValue((*modIter).first, (*modIter).second, true);
}
// init skill commands
{
const SkillObject::StringVector & sv = skill->getCommandsProvided ();
for (SkillObject::StringVector::const_iterator i = sv.begin();
i != sv.end(); ++i)
{
grantCommand(*i, true);
}
}
// init schematics
{
const SkillObject::StringVector & sv = skill->getSchematicsGranted ();
for (SkillObject::StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
grantSchematicGroup("+" + *i, true);
}
}
}
// read the non-skill data from the objvars and add those to the creature
// note that we add them as "from skill" so that they don't get added to the
// objvar list again
{
DynamicVariableList::NestedList mods(getObjVars(), OBJVAR_NOT_SKILL_MODS);
DynamicVariableList::NestedList::const_iterator i(mods.begin());
int value;
for (; i != mods.end(); ++i)
{
i.getValue(value);
addModValue(i.getName(), value, true);
}
}
{
DynamicVariableList::NestedList commands(getObjVars(), OBJVAR_NOT_SKILL_COMMANDS);
DynamicVariableList::NestedList::const_iterator i(commands.begin());
for (; i != commands.end(); ++i)
{
grantCommand(i.getName(), true);
}
if(getClient())
{
revokeCommand(AdminAccountManager::getAdminCommandName(), false);
if (getClient()->isGod())
grantCommand(AdminAccountManager::getAdminCommandName(), false);
}
}
{
DynamicVariableList::NestedList schematics(getObjVars(), OBJVAR_NOT_SKILL_SCHEMATICS);
DynamicVariableList::NestedList::const_iterator i(schematics.begin());
for (; i != schematics.end(); ++i)
{
grantSchematic(strtoul(i.getName().c_str(), NULL, 10), true);
}
}
// calculate the level based on the skill list
recalculateLevel();
}
} // CreatureObject::setupSkillData
//-----------------------------------------------------------------------
/**
* Creates a default controller for this object.
*
* @return the object's controller
*/
Controller* CreatureObject::createDefaultController ()
{
const ServerCreatureObjectTemplate * myTemplate = safe_cast<
const ServerCreatureObjectTemplate *>(getObjectTemplate());
Controller * controller = 0;
if (myTemplate->getCanCreateAvatar())
{
controller = new PlayerCreatureController(this);
}
else
{
controller = new AICreatureController(this);
AICreatureController * const aiCreatureController = AICreatureController::asAiCreatureController(controller);
if (aiCreatureController != NULL)
{
aiCreatureController->addServerNpAutoDeltaVariables(m_serverPackage_np);
}
}
setController(controller);
return controller;
} // CreatureObject::createDefaultController
//-----------------------------------------------------------------------
CreatureController* CreatureObject::getCreatureController()
{
return safe_cast<CreatureController*>(getController());
}
//----------------------------------------------------------------------
const CreatureController* CreatureObject::getCreatureController() const
{
return safe_cast<const CreatureController*>(getController());
}
//----------------------------------------------------------------------
void CreatureObject::setAuthority()
{
// don't recalculate our attribs until all our equipment has been made
// authoritative
++m_deferComputeTotalAttributes;
TangibleObject::setAuthority();
--m_deferComputeTotalAttributes;
computeTotalAttributes();
}
//----------------------------------------------------------------------
void CreatureObject::setAuthServerProcessId(uint32 processId)
{
uint32 const oldProcess = getAuthServerProcessId();
TangibleObject::setAuthServerProcessId(processId);
if (oldProcess != getAuthServerProcessId())
{
// we can't trade if we are on different servers, so cancel it
if (getCreatureController()->getSecureTrade() != NULL)
{
getCreatureController()->getSecureTrade()->cancelTrade(*this);
}
}
}
//----------------------------------------------------------------------
/**
* Updates the creature. Removes it from combat if it is incapacitated.
*
* @param time current game time
*
* @return base class result
*/
float CreatureObject::alter(float time)
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::alter");
float const result = TangibleObject::alter(time);
m_regionFlagTimer.updateNoReset(time); // Update our notify timer.
if (isPlayerControlled() && (isIncapacitated() || isDead()))
{
// If the player is trading, cancel the trade.
// We need to force the issue to catch edge cases.
ServerSecureTrade * const secureTradeObject = getCreatureController()->getSecureTrade();
if (secureTradeObject != NULL)
{
secureTradeObject->cancelTrade(*this);
}
}
if (!isAuthoritative())
{
// Non-authoritative activity.
removeFromSpawnQueue();
}
else
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::alter auth stuff");
//-- Mounts/vehicles: driver moves the server's mount/vehicle position to that of the driver.
transferRiderPositionToMount();
updateVehiclePhysicsData ();
// Authoritative activity.
// update the invulnerability timer
if (m_invulnerabilityTimer.get() != 0.0f)
{
float const timeLeft = std::max(0.f, m_invulnerabilityTimer.get()-time);
m_invulnerabilityTimer.set(timeLeft);
}
else
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::alter commandqueue");
// update the command queue
getCommandQueue()->update(time);
}
//check timer and migrate stats if necessary
//disable self stat migration
#if 0
migrateStats(time);
#endif
if (isPlayerControlled())
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::alter auth player stuff");
// keep the spawn queue pumping.
// todo: if the spawn queue is starving out players because
// there are too may other players on the queue, make some
// preliminary checks (e.g. in a city, too many creatures nearby)
// and reduce the spawn queue size
addToSpawnQueue();
{
PROFILER_AUTO_BLOCK_DEFINE("gradual customizations");
ServerImageDesignerManager::updateGradualCustomizations(this);
}
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
PROFILER_AUTO_BLOCK_DEFINE("playerobject stuff");
playerObject->alterPlayedTime(time);
playerObject->updateKillMeter(time);
playerObject->checkStomach();
playerObject->checkTheater(getPosition_w(), getSceneId(), getRunSpeed(), 128.0f);
updateGroup();
if (playerObject->getIsUnsticking() && getPositionChanged())
{
Chat::sendSystemMessageSimple(*this, SharedStringIds::unstick_request_cancelled, NULL);
playerObject->setIsUnsticking(false);
}
}
if (!ServerWorld::isSpaceScene())
{
// if we're in a conversation, end it if we move too far from the npc
if (m_npcConversation != NULL)
{
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::alter::npcConvCheck");
bool endConversation = false;
// check the distance to the npc
ServerObject const * const npc = safe_cast<ServerObject const *>(m_npcConversation->getNPC().getObject());
if (npc != NULL)
{
float distance = findPosition_w().magnitudeBetween(npc->findPosition_w());
distance -= getRadius();
distance -= npc->getRadius();
if (distance > ConfigServerGame::getNpcConversationDistance())
endConversation = true;
}
else
{
endConversation = true;
}
if (endConversation)
endNpcConversation();
}
}
}
alterPseudoPlayedTime(time);
updateBuffs();
//-- Handle mounts-specific functionality for an authoritative rider or mount.
alterAuthoritativeForMounts();
}
// see if we are performing a slow down effect
Property * property = getProperty(SlowDownProperty::getClassPropertyId());
if (property != NULL)
{
// has it expired?
SlowDownProperty * slowdown = safe_cast<SlowDownProperty *>(property);
if (slowdown->getExpireTime() <= ServerClock::getInstance().getGameTimeSeconds())
{
// yes, remove the effect
removeProperty(SlowDownProperty::getClassPropertyId());
}
else
{
// we need to find every authoritative non-player creature in the effect's
// area, and and tell them they are moving on our effect "hill" during their next alter
// (player creatures are handled on the player's client)
Object * target = slowdown->getTarget().getObject();
if (target != NULL)
{
std::vector<ServerObject *> found;
ServerWorld::findAuthoritativeNonPlayerCreaturesInCone(*this, *target, slowdown->getConeLength(), slowdown->getConeAngle(), found);
if (!found.empty())
{
// compute the effective normal vector of our "hill"
Vector normal(getPosition_w() - target->getPosition_w());
normal.y = 0;
normal.normalize();
normal *= cos(slowdown->getSlopeAngle() + PI_OVER_2);
normal.y = sin(slowdown->getSlopeAngle() + PI_OVER_2);
for (std::vector<ServerObject *>::iterator i = found.begin(); i != found.end(); ++i)
{
if ((*i)->getNetworkId() != getNetworkId())
safe_cast<CreatureObject*>(*i)->addTerrainSlopeEffect(normal);
}
}
}
}
}
//-- Handle mounts-specific functionality for all mounts. We'll do sanity checking in here.
alterAnyForMounts();
//-- Handle updateMovementInfo.
if (getLocalFlag(LocalObjectFlags::CreatureObject_RequiresMovementInfoUpdate))
{
updateMovementInfo();
setLocalFlag(LocalObjectFlags::CreatureObject_RequiresMovementInfoUpdate, false);
}
m_locomotion = calcLocomotion();
if (PlayerCreatureController::getPlayerObject(this) != 0)
{
if (m_level.get() != m_previousLevel)
{
if (isAuthoritative())
{
GameScriptObject * const script = getScriptObject();
if (script != 0)
{
ScriptParams params;
params.addParam(m_previousLevel);
params.addParam(m_level.get());
script->trigAllScripts(Scripting::TRIG_COMBAT_LEVEL_CHANGED, params);
}
}
m_previousLevel = m_level.get();
levelChanged();
}
}
return result;
}
//-----------------------------------------------------------------------
bool CreatureObject::canDestroy() const
{
// turn off our default weapon so it won't prevent us from getting
// destroyed
WeaponObject * defaultWeapon = getDefaultWeapon();
if (defaultWeapon != NULL)
defaultWeapon->setAsDefaultWeapon(false);
bool result = TangibleObject::canDestroy();
if (!result)
{
// we're not being destroyed, turn our default weapon back on
if (defaultWeapon != NULL)
defaultWeapon->setAsDefaultWeapon(true);
}
return result;
}
//-----------------------------------------------------------------------
float CreatureObject::getPvpRegionCheckTime()
{
return ConfigServerGame::getCreaturePvpRegionCheckTime();
}
//-----------------------------------------------------------------------
/**
* Sets the default weapon for a creature. This function bootstraps the default weapon.
* After this function is called, there should always be a weapon in the default
* weapon slot.
*/
void CreatureObject::initializeDefaultWeapon()
{
FATAL(!isAuthoritative(), ("CreatureObject::initializeDefaultWeapon: obj %s, while nonauth", getDebugInformation().c_str()));
ServerCreatureObjectTemplate const * const myTemplate = safe_cast<ServerCreatureObjectTemplate const *>(getObjectTemplate());
if (myTemplate != NULL)
{
ServerWeaponObjectTemplate const *weaponTemplate = myTemplate->getDefaultWeapon();
if (weaponTemplate == NULL)
{
WARNING(true, ("Creature template %s has no valid default weapon!", getTemplateName()));
// try to use the fallback weapon
weaponTemplate = dynamic_cast<ServerWeaponObjectTemplate const *>(ObjectTemplateList::fetch(ConfigServerGame::getFallbackDefaultWeapon()));
FATAL(weaponTemplate == NULL, ("CreatureObject::initializeFirstTimeObject fallback weapon template %s is bad!", ConfigServerGame::getFallbackDefaultWeapon()));
}
WeaponObject * const weapon = safe_cast<WeaponObject *>(ServerWorld::createNewObject(*weaponTemplate, *this, s_defaultWeaponSlotId, false));
if (weapon != NULL)
weapon->setAsDefaultWeapon(true);
else
{
WARNING_STRICT_FATAL(true, ("CreatureObject::initializeDefaultWeapon unable to create default weapon %s for creature %s", weaponTemplate->getName(), getNetworkId().getValueString().c_str()));
}
weaponTemplate->releaseReference();
}
}
// ----------------------------------------------------------------------
bool CreatureObject::swapDefaultWeapons(WeaponObject &newDefaultWeapon, ServerObject &weaponContainer)
{
FATAL(!isAuthoritative(), ("CreatureObject::swapDefaultWeapons: obj %s, weapon %s, weaponContainer %s, while obj nonauth", getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str()));
FATAL(!newDefaultWeapon.isAuthoritative(), ("CreatureObject::swapDefaultWeapons: obj %s, weapon %s, weaponContainer %s, while newDefaultWeapon nonauth", getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str()));
FATAL(!weaponContainer.isAuthoritative(), ("CreatureObject::swapDefaultWeapons: obj %s, weapon %s, weaponContainer %s, while weaponContainer nonauth", getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str()));
// There is a window here where the default weapon can be null, so we
// set a flag that it's ok until we've finished the transfer.
FATAL(s_allowNullDefaultWeapon, ("CreatureObject::swapDefaultWeapons has been recursively called!"));
WeaponObject * const oldDefaultWeapon = getDefaultWeapon();
FATAL(!oldDefaultWeapon, ("CreatureObject::swapDefaultWeapons: obj %s, weapon %s, weaponContainer %s, with no current default weapon", getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str()));
s_allowNullDefaultWeapon = true;
// Move the current default weapon into the weapon container
oldDefaultWeapon->setAsDefaultWeapon(false);
Container::ContainerErrorCode err = Container::CEC_Success;
bool const success1 = ContainerInterface::transferItemToVolumeContainer(weaponContainer, *oldDefaultWeapon, 0, err, true);
FATAL(!success1, ("CreatureObject::swapDefaultWeapons: obj %s, old default weapon %s, new default weapon %s, weaponContainer %s, old default weapon transfer to weaponContainer failed with error %d", getDebugInformation().c_str(), oldDefaultWeapon->getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str(), static_cast<int>(err)));
// Try to move the new default weapon into the default weapon slot
bool const success2 = ContainerInterface::transferItemToSlottedContainer(*this, newDefaultWeapon, s_defaultWeaponSlotId, 0, err);
if (success2)
newDefaultWeapon.setAsDefaultWeapon(true);
else
{
// We failed to swap in the new default weapon, so we need to move the old one back in.
bool const success3 = ContainerInterface::transferItemToSlottedContainer(*this, *oldDefaultWeapon, s_defaultWeaponSlotId, 0, err);
FATAL(!success3, ("CreatureObject::swapDefaultWeapons: obj %s, old default weapon %s, new default weapon %s, weaponContainer %s, old default weapon transfer back to default weapon slot after failure to switch to new default weapon failed with error %d", getDebugInformation().c_str(), oldDefaultWeapon->getDebugInformation().c_str(), newDefaultWeapon.getDebugInformation().c_str(), weaponContainer.getDebugInformation().c_str(), static_cast<int>(err)));
oldDefaultWeapon->setAsDefaultWeapon(true);
}
s_allowNullDefaultWeapon = false;
return success2;
}
// ----------------------------------------------------------------------
WeaponObject *CreatureObject::getDefaultWeapon() const
{
SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this);
if (container == NULL)
{
return NULL;
}
WeaponObject * defaultWeapon = NULL;
Container::ContainerErrorCode error;
Container::ContainedItem itemId = container->getObjectInSlot(s_defaultWeaponSlotId, error);
if (error == Container::CEC_Success)
{
Object * const o = itemId.getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
defaultWeapon = so->asWeaponObject();
}
FATAL(!s_allowNullDefaultWeapon && defaultWeapon == NULL, ("CreatureObject::getDefaultWeapon, weapon is NULL! Object in default slot is %s", itemId.getValueString().c_str()));
}
return defaultWeapon;
}
// ----------------------------------------------------------------------
/**
* Returns an attribute value, modified by attribute mods.
*
* @param attribute the attribute to get
*/
Attributes::Value CreatureObject::getAttribute(Attributes::Enumerator attribute) const
{
if (attribute < 0 || attribute >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attribute));
return 0;
}
return getAdjustedAttribute(attribute, m_attributes[attribute]);
}
//-----------------------------------------------------------------------
/**
* Returns an attribute value, adjusted by wounds, etc.
*
* @param attribute the attribute type
* @param value the value to adjust
*
* @returns value adjusted by various params
*/
Attributes::Value CreatureObject::getAdjustedAttribute(Attributes::Enumerator attribute,
Attributes::Value value) const
{
if (attribute < 0 || attribute >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attribute));
return value;
}
// modify the attribute for attrib mods
value = static_cast<Attributes::Value>(
value + m_cachedCurrentAttributeModValues[attribute]);
// modify for partial regeneration
value = static_cast<Attributes::Value>(value + static_cast<int>(floor(m_regeneration[attribute])));
//cap maximum value
Attributes::Value max = getMaxAttribute(attribute);
if (value > max)
value = max;
return value;
} // CreatureObject::getAdjustedAttribute
//-----------------------------------------------------------------------
/**
* Returns a max attribute value, modified by attribute mods.
*
* @param attribute the attribute to get
*/
Attributes::Value CreatureObject::getMaxAttribute(Attributes::Enumerator attribute, bool capStat) const
{
if (attribute < 0 || attribute >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attribute));
return 0;
}
Attributes::Value value = getUnmodifiedMaxAttribute(attribute);
value = static_cast<Attributes::Value>(
value + m_cachedMaxAttributeModValues[attribute] + m_attribBonus[attribute]);
if (capStat)
{
// make sure the value doesn't go below the defined minimum value
if (Attributes::isAttribPool(attribute) && value < ConfigServerGame::getMinPoolValue())
value = static_cast<Attributes::Value>(ConfigServerGame::getMinPoolValue());
// double-check that the max isn't <= 0
if (value < 1)
value = 1;
}
return value;
}
//-----------------------------------------------------------------------
const std::map<std::string, std::pair<int, int> > & CreatureObject::getModMap() const
{
return m_modMap.getMap();
}
//-----------------------------------------------------------------------
const int CreatureObject::getModValue(const std::string & modName) const
{
int result = 0;
std::map<std::string, std::pair<int, int> >::const_iterator f = m_modMap.find(modName);
if(f != m_modMap.end())
{
result = (*f).second.first;
}
return result;
}
//-----------------------------------------------------------------------
/**
* Returns the value for a skill mod, modified by any special equipment the
* creature may be wearing.
*
* @param statMod the name of the skill mod
*
* @return the mod value
*/
const int CreatureObject::getEnhancedModValue(const std::string & modName) const
{
int base = 0;
int bonus = 0;
std::map<std::string, std::pair<int, int> >::const_iterator f = m_modMap.find(modName);
if (f != m_modMap.end())
{
base = (*f).second.first;
bonus = (*f).second.second;
}
if (bonus > ConfigSharedGame::getMaxCreatureSkillModBonus())
bonus = ConfigSharedGame::getMaxCreatureSkillModBonus();
return base + bonus;
} // CreatureObject::getEnhancedSkillStatMod
//-----------------------------------------------------------------------
/**
* Returns the value for a skill mod, modified by any special equipment the
* creature may be wearing. This version does not respect getMaxCreatureSkillModBonus
*
* @param statMod the name of the skill mod
*
* @return the mod value
*/
const int CreatureObject::getEnhancedModValueUncapped(const std::string & modName) const
{
int base = 0;
int bonus = 0;
std::map<std::string, std::pair<int, int> >::const_iterator f = m_modMap.find(modName);
if (f != m_modMap.end())
{
base = (*f).second.first;
bonus = (*f).second.second;
}
return base + bonus;
} // CreatureObject::getEnhancedSkillStatMod
//-----------------------------------------------------------------------
void CreatureObject::setModValue(const std::string & modName, const int value)
{
if(isAuthoritative())
{
int oldBonus = 0;
Archive::AutoDeltaMap<std::string, std::pair<int, int> >::const_iterator found =
m_modMap.find(modName);
if (found != m_modMap.end())
oldBonus = (*found).second.second;
if (value != 0 || oldBonus != 0)
{
m_modMap.set(modName, std::make_pair(value, oldBonus));
}
else
{
m_modMap.erase(modName);
}
updateSlopeMovement(modName);
}
else
{
sendControllerMessageToAuthServer(CM_setModValue, new MessageQueueGenericValueType<std::pair<std::string, int> >(std::make_pair(modName, value)));
}
}
//-----------------------------------------------------------------------
void CreatureObject::addModValue(const std::string & modName, const int value, bool fromSkill)
{
int currentVal = getModValue(modName);
setModValue(modName, currentVal + value);
if (!fromSkill)
{
// update the objvars for the value
std::string objvarName(OBJVAR_NOT_SKILL_MODS + "." + modName);
int currentValue = 0;
getObjVars().getItem(objvarName, currentValue);
currentValue += value;
if (currentValue != 0)
setObjVarItem(objvarName, currentValue);
else
removeObjVarItem(objvarName);
}
}
// ----------------------------------------------------------------------
/**
* Adds a skill mod bonus to the creature.
*
* @param statMod the skill mod name
* @param bonus the bonus value
*/
void CreatureObject::addModBonus(const std::string & modName, int bonus)
{
if(isAuthoritative())
{
int oldBase = 0;
int oldBonus = 0;
Archive::AutoDeltaMap<std::string, std::pair<int, int> >::const_iterator found =
m_modMap.find(modName);
if (found != m_modMap.end())
{
oldBase = (*found).second.first;
oldBonus = (*found).second.second;
}
bonus += oldBonus;
if (bonus != 0 || oldBase != 0)
{
m_modMap.set(modName, std::make_pair(oldBase, bonus));
}
else
{
m_modMap.erase(modName);
}
updateSlopeMovement(modName);
}
else
{
// sendControllerMessageToAuthServer(CM_setModValue, new MessageQueueGenericValueType<std::pair<std::string, int> >(std::make_pair(modName, value)));
}
} // CreatureObject::addModBonus
// ----------------------------------------------------------------------
/**
* Adds an attrib bonus to the creature.
*
* @param attrib the attribute
* @param bonus the bonus value
*/
void CreatureObject::addAttribBonus(int attrib, int bonus)
{
if (isAuthoritative())
{
if (attrib < 0 || attrib >= Attributes::NumberOfAttributes)
return;
int newBonus = m_attribBonus[attrib] + bonus;
if (newBonus > ConfigServerGame::getMaxTotalAttribBonus())
newBonus = ConfigServerGame::getMaxTotalAttribBonus();
else if (newBonus < -ConfigServerGame::getMaxTotalAttribBonus())
newBonus = -ConfigServerGame::getMaxTotalAttribBonus();
m_attribBonus.set(attrib, newBonus);
}
} // CreatureObject::addAttribBonus
// ----------------------------------------------------------------------
void CreatureObject::updateSlopeMovement(const std::string & modName)
{
// if I'm modifiying the slope mod, update my speed
if (modName == SLOPE_MOD)
recomputeSlopeModPercent();
// if I'm modifiying the group slope mod and I'm a group leader,
// update my group's speed
else if (modName == GROUP_SLOPE_MOD && getGroup() != NULL &&
getGroup()->getGroupLeaderId() == getNetworkId())
{
const GroupObject::GroupMemberVector & members = getGroup()->getGroupMembers();
GroupObject::GroupMemberVector::const_iterator iter;
for (iter = members.begin(); iter != members.end(); ++iter)
{
CreatureObject * member = safe_cast<CreatureObject *>(
NetworkIdManager::getObjectById((*iter).first));
if (member != NULL)
member->recomputeSlopeModPercent();
}
}
}
// ----------------------------------------------------------------------
/**
* Cause the client to dance using the specified dance style.
*
* This function will change the server and clients to the SkillAnimating
* posture.
*
* @param enabled if true, start doing the skill. If false, stop doing the skill.
* @param danceStyle the number of the dance style.
*
* @return true if this function succeeded, false if it failed for any reason.
*/
bool CreatureObject::showDanceVisuals(bool enabled)
{
//-- Ensure we are authoratative. This function should only be called
// indirectly via the command table, which guarantees it is called on the
// authoratative object.
if (!isAuthoritative())
{
DEBUG_WARNING(true, ("showDanceVisuals() should be called only on the authoratative object."));
return false;
}
if (!enabled)
{
//-- Set the server object to the upright posture.
setPosture(Postures::Upright);
//-- Tell all clients to switch to the specified posture.
Controller *const controller = getController();
if (controller)
controller->appendMessage(CM_setPosture, 0.0f, new MessageQueuePosture(Postures::Upright, true), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
else
DEBUG_WARNING(true, ("showDanceVisuals(): no controller, could not tell clients to go to SkillAnimating posture."));
}
else
{
int visualId = lookupDanceVisual(getPerformanceType());
if (visualId == 0)
return false;
//-- Set the server object to animating skill posture.
setPosture(Postures::SkillAnimating);
//-- Tell all clients to switch to the specified posture.
Controller *const controller = getController();
if (controller)
controller->appendMessage(CM_setPosture, 0.0f, new MessageQueuePosture(Postures::SkillAnimating, true), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
else
DEBUG_WARNING(true, ("showDanceVisuals(): no controller, could not tell clients to go to SkillAnimating posture."));
//-- Modify the animating skill data string.
char buffer[64];
sprintf(buffer, "dance_%d", visualId);
m_animatingSkillData = buffer;
}
//-- Success.
return true;
}
// ----------------------------------------------------------------------
bool CreatureObject::showMusicianVisuals(bool enabled)
{
//-- Ensure we are authoratative. This function should only be called
// indirectly via the command table, which guarantees it is called on the
// authoratative object.
if (!isAuthoritative())
{
DEBUG_WARNING(true, ("showMusicianVisuals() should be called only on the authoratative object."));
return false;
}
if (!enabled)
{
//-- Set the server object to the upright posture.
setPosture(Postures::Upright);
//-- Tell all clients to switch to the specified posture.
Controller *const controller = getController();
if (controller)
controller->appendMessage(CM_setPosture, 0.0f, new MessageQueuePosture(Postures::Upright, true), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
else
DEBUG_WARNING(true, ("showDanceVisuals(): no controller, could not tell clients to go to SkillAnimating posture."));
}
else
{
//-- Determine the type of instrument used by this creature.
// @todo
const int instrumentId = getInstrumentVisualId();
if (instrumentId == 0)
{
WARNING(true, ("tried to call showMusicianVisuals without a valid instrument"));
return false;
}
//-- Set the server object to animating skill posture.
setPosture(Postures::SkillAnimating);
//-- Tell all clients to switch to the specified posture.
Controller *const controller = getController();
if (controller)
controller->appendMessage(CM_setPosture, 0.0f, new MessageQueuePosture(Postures::SkillAnimating, true), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
else
DEBUG_WARNING(true, ("showDanceVisuals(): no controller, could not tell clients to go to SkillAnimating posture."));
//-- Modify the animating skill data string.
char buffer[64];
sprintf(buffer, "music_%d", instrumentId);
m_animatingSkillData = buffer;
}
//-- Success.
return true;
}
//-----------------------------------------------------------------------
void CreatureObject::sendMusicFlourish(int flourishIndex)
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<int> *msg = new MessageQueueGenericValueType<int>(flourishIndex);
controller->appendMessage(
CM_musicFlourish,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_ALL_CLIENT);
}
}
//-----------------------------------------------------------------------
/**
* Returns a mental state value, modified by mental state mods.
*
* @param attribute the attribute to get
*/
MentalStates::Value CreatureObject::getMentalState(MentalStates::Enumerator attribute) const
{
if (attribute < 0 || attribute >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", attribute));
return 0.0f;
}
MentalStates::Value value = static_cast<MentalStates::Value>(m_currentAttitude.get().m_currentValues[attribute]);
return value;
}
//-----------------------------------------------------------------------
/**
* Returns a mental state value, modified by mental state mods.
*
* @param attribute the attribute to get
*/
MentalStates::Value CreatureObject::getMentalStateToward(const NetworkId &target, MentalStates::Enumerator attribute) const
{
if (attribute < 0 || attribute >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", attribute));
return 0.0f;
}
MentalStates::Value value = static_cast<MentalStates::Value>(m_currentAttitude.get().m_currentValues[attribute]);
CreatureAttitude const * const cs = getAttitudeToward(target);
if (cs)
{
value = static_cast<MentalStates::Value>(value + cs->m_currentValues[attribute]);
}
return value;
}
//-----------------------------------------------------------------------
int CreatureObject::getExperiencePoints(const std::string & experienceType) const
{
if (isPlayerControlled())
{
const PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->getExperiencePoints(experienceType);
}
return 0;
}
//-----------------------------------------------------------------------
const std::map<std::string, int> & CreatureObject::getExperiencePoints() const
{
static const std::map<std::string, int> emptyResult;
if(isPlayerControlled())
{
const PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if(playerObject != NULL)
{
return playerObject->getExperiencePoints();
}
}
return emptyResult;
}
//-----------------------------------------------------------------------
const CreatureObject::SkillList & CreatureObject::getSkillList() const
{
return m_skills.get();
}
//-----------------------------------------------------------------------
const int CreatureObject::grantExperiencePoints(const std::string & experienceType, int amount)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
{
int const amountGranted = playerObject->grantExperiencePoints(experienceType, amount);
LevelManager::LevelData levelData(m_level.get(), m_totalLevelXp.get());
LevelManager::addXpToLevelData(levelData, experienceType, amountGranted);
setLevelData(levelData.currentLevel, levelData.currentLevelXp, levelData.currentHealth);
return amountGranted;
}
}
return INT_MIN;
}
// ----------------------------------------------------------------------
const bool CreatureObject::grantSkill(const SkillObject & newSkill)
{
if(isAuthoritative())
{
//-- jww: simply return true if the skill is already in the vector
//-- this was the previous behavior, dunno if it should return false
if (hasSkill (newSkill))
return true;
// signal scripts that skill is about to be granted. If the script returns override, cancel skill grant.
{
ScriptParams params;
params.addParam(newSkill.getSkillName().c_str());
if (getScriptObject()->trigAllScripts(Scripting::TRIG_SKILL_ABOUT_TO_BE_GRANTED, params) == SCRIPT_OVERRIDE)
{
return false;
}
}
m_skills.insert(&newSkill);
if(getClient())
{
LOG("CustomerService", ("Skill: Account %s CharacterId %s has acquired skill %s", getClient()->getAccountName().c_str(), getNetworkId().getValueString().c_str(), newSkill.getSkillName().c_str()));
}
{
const SkillObject::StringVector & sv = newSkill.getCommandsProvided ();
for (SkillObject::StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
grantCommand(*i, true);
}
const std::vector<std::pair<std::string, int> > & statMods = newSkill.getStatisticModifiers();
std::vector<std::pair<std::string, int> >::const_iterator modIter;
for(modIter = statMods.begin(); modIter != statMods.end(); ++ modIter)
{
addModValue((*modIter).first, (*modIter).second, true);
}
{
const SkillObject::StringVector & sv = newSkill.getSchematicsGranted ();
for (SkillObject::StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
grantSchematicGroup("+" + *i, true);
}
// signal scripts that skill has been granted
ScriptParams params;
params.addParam(newSkill.getSkillName().c_str());
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_SKILL_GRANTED,
params));
LevelManager::LevelData levelData(m_level.get(), m_totalLevelXp.get());
LevelManager::addSkillToLevelData(levelData, newSkill.getSkillName());
setLevelData(levelData.currentLevel, levelData.currentLevelXp, levelData.currentHealth);
}
else
{
sendControllerMessageToAuthServer(CM_grantSkill, new MessageQueueGenericValueType<std::string>(newSkill.getSkillName()));
}
return true;
}
//-----------------------------------------------------------------------
void CreatureObject::revokeSkill(const SkillObject & oldSkill, bool silent)
{
if(isAuthoritative())
{
// signal scripts that skill has been revoked
ScriptParams params;
params.addParam(oldSkill.getSkillName().c_str());
if(getScriptObject()->trigAllScripts(Scripting::TRIG_SKILL_ABOUT_TO_BE_REVOKED, params) == SCRIPT_CONTINUE)
{
for (SkillList::const_iterator it = m_skills.begin(); it != m_skills.end(); ++it)
{
const SkillObject * const skill = *it;
if (skill)
{
const SkillObject::SkillVector &prerequisiteSkills = skill->getPrerequisiteSkills();
for (SkillObject::SkillVector::const_iterator pit = prerequisiteSkills.begin(); pit != prerequisiteSkills.end(); ++pit)
{
const SkillObject * const prereq = *pit;
NOT_NULL (prereq);
if (oldSkill.getSkillName() == prereq->getSkillName())
{
if (!silent)
Chat::sendSystemMessage(*this, SharedStringIds::revoke_dependant_skill, Unicode::emptyString);
return;
}
}
}
}
if (m_skills.contains(&oldSkill))
{
// remove granted commands
{
const SkillObject::StringVector & sv = oldSkill.getCommandsProvided ();
for (SkillObject::StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
revokeCommand(*i, true);
}
// remove granted skill mods
const std::vector<std::pair<std::string, int> > & statMods = oldSkill.getStatisticModifiers();
std::vector<std::pair<std::string, int> >::const_iterator modIter;
for(modIter = statMods.begin(); modIter != statMods.end(); ++ modIter)
{
addModValue((*modIter).first, -(*modIter).second, true);
}
// remove schematics that were granted by any schematic groups
{
const SkillObject::StringVector & sv = oldSkill.getSchematicsGranted ();
for (SkillObject::StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
{
const std::string & grantedGroup = *i;
grantSchematicGroup("-" + grantedGroup, true);
}
}
m_skills.erase(&oldSkill);
if(getClient())
{
LOG("CustomerService", ("Skill: Account %s CharacterId %s has lost skill %s", getClient()->getAccountName().c_str(), getNetworkId().getValueString().c_str(), oldSkill.getSkillName().c_str()));
}
// Remove the current skill title if this is a player and their current title
// is this skill
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if ((playerObject != NULL) &&
(oldSkill.getSkillName() == playerObject->getTitle()))
{
StringId message("shared", "skill_title_removed");
Unicode::String outOfBand;
if (!silent)
Chat::sendSystemMessage(*this, message, outOfBand);
playerObject->setTitle("");
}
// signal scripts that skill has been revoked
ScriptParams params;
params.addParam(oldSkill.getSkillName().c_str());
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_SKILL_REVOKED,params));
// update the level data
LevelManager::LevelData levelData(m_level.get(), m_totalLevelXp.get());
LevelManager::removeSkillFromLevelData(levelData, oldSkill.getSkillName());
setLevelData(levelData.currentLevel, levelData.currentLevelXp, levelData.currentHealth);
}
}
}
else
{
LOG("CustomerService", ("Skill: Could not revoke skill %s from character %s because it is a proxy so we're forwarding this to the auth server.",
oldSkill.getSkillName().c_str(), getNetworkId().getValueString().c_str()));
sendControllerMessageToAuthServer(CM_revokeSkill, new MessageQueueGenericValueType<std::string>(oldSkill.getSkillName()));
}
}
//-----------------------------------------------------------------------
const bool CreatureObject::grantSchematicGroup(const std::string & groupNameWithModifier, bool fromSkill)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->grantSchematicGroup(groupNameWithModifier, fromSkill);
}
return false;
}
//-----------------------------------------------------------------------
const bool CreatureObject::grantSchematic(const std::string & schematicName, bool fromSkill)
{
return grantSchematic(Crc::calculate(schematicName.c_str()), fromSkill);
}
//-----------------------------------------------------------------------
const bool CreatureObject::grantSchematic(uint32 schematicCrc, bool fromSkill)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->grantSchematic(schematicCrc, fromSkill);
}
return false;
}
//-----------------------------------------------------------------------
const bool CreatureObject::revokeSchematic(const std::string & schematicName, bool fromSkill)
{
return revokeSchematic(Crc::calculate(schematicName.c_str()), fromSkill);
}
//-----------------------------------------------------------------------
const bool CreatureObject::revokeSchematic(uint32 schematicCrc, bool fromSkill)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->revokeSchematic(schematicCrc, fromSkill);
}
return false;
}
//-----------------------------------------------------------------------
const bool CreatureObject::hasSchematic(const std::string & schematicName)
{
return hasSchematic(Crc::calculate(schematicName.c_str()));
}
//-----------------------------------------------------------------------
const bool CreatureObject::hasSchematic(uint32 schematicCrc)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->hasSchematic(schematicCrc);
}
return false;
}
//-----------------------------------------------------------------------
const bool CreatureObject::hasSkill(const SkillObject & skill) const
{
return std::find (m_skills.begin(), m_skills.end (), &skill) != m_skills.end();
}
//-----------------------------------------------------------------------
/**
* Sets up the object for being in or out of combat.
* @todo: this should really be in tangible only, we need to have a command queue
* for tangibles
*
* @param inCombat flag to put the object in combat-ready state or not
*
* @return true if state was set, false if something went wrong
*/
void CreatureObject::setInCombat(bool inCombat)
{
TangibleObject::setInCombat(inCombat);
if (isAuthoritative())
{
if (!inCombat)
{
setState(States::Aiming, false);
}
else
{
// if we are crafting, end the crafting session
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL && player->isCrafting())
{
player->stopCrafting(false);
}
}
}
}
//-----------------------------------------------------------------------
/**
* If this object is in combat, adds an aim to it.
*/
void CreatureObject::addAim ()
{
TangibleObject::addAim();
if (isInCombat())
{
setState(States::Aiming, true);
}
} // CreatureObject::addAim
//-----------------------------------------------------------------------
/**
* If this object is in combat, clear's it's aims.
*/
void CreatureObject::clearAims ()
{
TangibleObject::clearAims();
if (isInCombat())
{
setState(States::Aiming, false);
}
} // CreatureObject::clearAims
//-----------------------------------------------------------------------
/**
* Modifies a creature's attribute. No other function should modify the pool indexes
* of m_attributes. Will incapacitate the creature if an attribute drops to 0.
*
* @param attrib the attribute to modify
* @param delta amount to modify the attribute by
* @param checkIncapacitation flag to check for incap/recap
* @param source who is doing the damage (NetworkId::cms_invalid
* for no one)
* @param force force the change, even if we would normally not
* make it
*
* @return the amount of damage actually applied
*/
int CreatureObject::alterAttribute(Attributes::Enumerator attrib, int delta,
bool checkIncapacitation, const NetworkId & source, bool force)
{
if (isAuthoritative())
{
if (attrib < 0 || attrib >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attrib));
return 0;
}
if (!Attributes::isAttribPool(attrib))
{
// we only alter pool values now, regeneration is set directly
DEBUG_REPORT_LOG(true, ("CreatureObject::alterAttribute called on %s for non-pool attrib (%d)\n",
getNetworkId().getValueString().c_str(), attrib));
return 0;
}
if (delta == 0)
return 0;
// Use TangibleObject::isInvulnerable so we don't consider invulnerability timers
if ((isDead() || TangibleObject::isInvulnerable()) && !force)
return 0;
if (getMaxAttribute(attrib) <= 0 && !force)
return 0;
// if we are taking damage, try and damage any attrib mods instead
int attribModChange = 0;
int regenChange = 0;
if (delta < 0)
{
attribModChange = changeAttribModCurrentValue(attrib, delta, false);
if (attribModChange == delta)
{
computeTotalAttributes();
if (checkIncapacitation)
testIncapacitation(source);
return attribModChange;
}
else
{
delta -= attribModChange;
}
// if we are still taking damage, try and reduce any accumulated
// regeneration points
if (delta < 0 && m_regeneration[attrib] > 0)
{
if (m_regeneration[attrib] >= -delta)
{
// regen can absorb it all
m_regeneration[attrib] += delta;
computeTotalAttributes();
if (checkIncapacitation)
testIncapacitation(source);
return attribModChange + delta;
}
else
{
regenChange = -static_cast<int>(floor(m_regeneration[attrib]));
m_regeneration[attrib] += regenChange;
delta -= regenChange;
}
}
}
// Don't set the actual attribute until the end of this function,
// to avoid changing it multiple times and generating network spam
int newValue = m_attributes[attrib] + delta;
// if the attribute is more than the max attribute, reduce it
int maxAttribLimit = getUnmodifiedMaxAttribute(attrib) + getAttributeBonus(attrib);
if (newValue > maxAttribLimit)
{
int overflow = newValue - maxAttribLimit;
delta -= overflow;
newValue = maxAttribLimit;
// try and heal any attrib mods
attribModChange = changeAttribModCurrentValue(attrib, overflow, false);
}
// cap for min attrib value, depending on attrib type
int minValue = 0;
if (isPlayerControlled())
{
if (Attributes::isAttribPool(attrib))
minValue = ConfigServerGame::getMinPoolValue();
}
int adjustedNewValue = getAdjustedAttribute(attrib, static_cast<
Attributes::Value>(newValue));
if (adjustedNewValue < minValue)
{
newValue = minValue - (adjustedNewValue - newValue);
adjustedNewValue = minValue;
#ifdef _DEBUG
adjustedNewValue = getAdjustedAttribute(attrib, static_cast<
Attributes::Value>(newValue));
if (adjustedNewValue != minValue)
{
DEBUG_WARNING(true, ("CreatureObject::alterAttribute, "
"adjustedNewValue != minValue"));
}
#endif
}
if (newValue != m_attributes[attrib])
{
// clip delta so whoever did the damage won't get overkill xp
if (adjustedNewValue < 0)
adjustedNewValue = 0;
delta = adjustedNewValue - getAttribute(attrib);
// change the attribute
m_attributes.set(attrib, static_cast<Attributes::Value>(newValue));
// update the client and check for incapacitation
computeTotalAttributes();
if (checkIncapacitation)
testIncapacitation(source);
}
else
delta = 0;
return delta + attribModChange + regenChange;
}
else
{
sendControllerMessageToAuthServer(CM_alterAttribute,
new MessageQueueAlterAttribute(attrib, delta, checkIncapacitation,
source));
return 0;
}
} // CreatureObject::alterAttribute
//-----------------------------------------------------------------------
/**
* Tests to see if we need to be incapacitated/recapacitated.
*
* @param attackerId the object that may have caused our incapacitation.
*/
void CreatureObject::testIncapacitation(const NetworkId & attackerId)
{
WARNING_STRICT_FATAL(!isAuthoritative(), ("CreatureObject::testIncapacitation invoked on non-authoritative object %s:%s", getObjectTemplateName(), getNetworkId().getValueString().c_str()));
if (m_deferComputeTotalAttributes <= 0)
{
int const health = getAttribute(Attributes::Health);
if (!isIncapacitated())
{
// see if we are incapacitated
if (health <= 0)
{
setIncapacitated(true, attackerId);
}
}
else
{
// recapacitate if any attribute is above 0
if (health > 0)
{
setIncapacitated(false, attackerId);
}
}
}
}
//-----------------------------------------------------------------------
/**
* Applies an attribute modifier to this creature.
*
* @param name the attrib mod name
* @param attrib the attribute to modify
* @param value the value to apply
* @param duration how long the modifier lasts
* @param attackRate time to get to the peak modifier value
* @param decayRate time for the modifier to be reduced to 0
* @param flags flags that define how the mod works
*/
void CreatureObject::addAttributeModifier(const std::string & name,
Attributes::Enumerator attrib, int value, float duration, float attackRate,
float decayRate, int flags)
{
AttribMod::AttribMod mod;
if (!name.empty())
{
AttribModNameManager::getInstance().addAttribModName(name.c_str());
mod.tag = Crc::calculate(name.c_str());
}
else
mod.tag = 0;
mod.attrib = attrib;
mod.value = value;
mod.attack = attackRate;
mod.sustain = duration;
mod.decay = decayRate;
mod.flags = flags;
if (!Attributes::isAttribPool(mod.attrib))
{
// non-pool mods must change both max and current
mod.flags |=
AttribMod::AMF_changeMax |
AttribMod::AMF_attackCurrent |
AttribMod::AMF_decayCurrent;
}
LOG("CustomerService", ("Attribs: Added attrib mod to %s. Mod data: "
"%s(%u) %d %d %.2f %.2f %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
name.c_str(), mod.tag, mod.attrib, mod.value, mod.attack, mod.sustain,
mod.decay, mod.flags));
addAttributeModifier(mod);
} // CreatureObject::addAttributeModifier
//-----------------------------------------------------------------------
/**
* Applies a skillmod modifier to this creature.
*
* @param name the attrib mod name
* @param skill the skillmod to modify
* @param value the value to apply
* @param duration how long the modifier lasts (-1 = infinite, or until the player logs off)
* @param flags flags that define how the mod works
*/
void CreatureObject::addSkillmodModifier(const std::string & name,
const std::string & skill, int value, float duration, int flags)
{
AttribMod::AttribMod mod;
if (skill.empty())
{
WARNING_STRICT_FATAL(true, ("CreatureObject::addSkillmodModifier called "
"with empty string for skill"));
return;
}
if (!name.empty())
{
AttribModNameManager::getInstance().addAttribModName(name.c_str());
mod.tag = Crc::calculate(name.c_str());
}
else
mod.tag = 0;
AttribModNameManager::getInstance().addAttribModName(skill.c_str());
mod.skill = Crc::calculate(skill.c_str());
mod.value = value;
mod.attack = 0;
mod.sustain = duration;
mod.decay = 0;
mod.flags = flags | AttribMod::AMF_skillMod;
LOG("CustomerService", ("Attribs: Added skill mod to %s. Mod data: "
"%s(%u) %s(%u) %d %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
name.c_str(), mod.tag, skill.c_str(), mod.skill,
mod.value, mod.sustain, mod.flags));
addAttributeModifier(mod);
} // CreatureObject::addSkillmodModifier
//-----------------------------------------------------------------------
/**
* Applies an attribute modifier to this creature.
*
* @param mod the mod to add
* @param attacker who is causing the mod to be applied
*
* @return the amount the attrib was modified by, if the mod did direct damage
*/
int CreatureObject::addAttributeModifier(const AttribMod::AttribMod & mod,
ServerObject * attacker)
{
static char internalTagBuf[] = {'_','_','i','n','t','e','r','n','a','l','T','a','g','N','a','m','e','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','\0'};
static const int internalTagBufLen = strlen(internalTagBuf);
int damage = 0;
if (isAuthoritative())
{
++m_deferComputeTotalAttributes;
if (AttribMod::isDirectDamage(mod))
{
// check if we're making a potentially dangerous mod
if (!Attributes::isAttribPool(mod.attrib))
{
WARNING(true, ("Creature %s is having non-pool attribute %d instantly "
"modified! This change will be permanent!",
getNetworkId().getValueString().c_str(), mod.attrib));
}
// apply instantanious damage directly
#ifdef _DEBUG
if (ConfigCombatEngine::getDebugDamage())
{
char debugBuffer[1024];
snprintf(debugBuffer, 1024, "creature %s attribute %d(%d) modified by %d to %d\n",
getNetworkId().getValueString().c_str(),
mod.attrib,
m_attributes[mod.attrib],
mod.value,
m_attributes[mod.attrib] + mod.value);
DEBUG_REPORT_LOG (true, ("%s", debugBuffer));
if (attacker != NULL)
{
Client *client = attacker->getClient();
if (client != NULL)
ConsoleMgr::broadcastString(debugBuffer, client);
}
}
#endif
damage = alterAttribute(mod.attrib, mod.value, false,
attacker ? attacker->getNetworkId() : NetworkId::cms_invalid);
}
else if (AttribMod::isAttribMod(mod) && mod.decay == AttribMod::AMDS_antidote)
{
removeAttributeModifiers(mod.attrib);
}
else
{
if (mod.attack >= 0 && (mod.sustain >= 0 || mod.sustain == -1.0f))
{
// apply other damage to the attribMod list
CreatureMod newMod;
newMod.startTime = ServerClock::getInstance().getGameTimeSeconds();
newMod.maxVal = 0;
newMod.currentVal = 0;
newMod.mod = mod;
if (newMod.mod.tag == 0)
{
// we need to have some sort of tag, so we use an internal
// name hash
std::pair<Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator, bool> insertResult;
do
{
newMod.mod.tag = Crc::calculate(&internalTagBuf[0]);
char * c = &internalTagBuf[internalTagBufLen-1];
while (++(*c) == '9' + 1)
{
*c-- = '0';
}
insertResult = m_attributeModList.insert(newMod.mod.tag, newMod);
} while (!insertResult.second);
// clear any flags that assume we have a valid tag.
if (newMod.mod.flags & AttribMod::AMF_triggerOnDone)
{
newMod.mod.flags &= ~AttribMod::AMF_triggerOnDone;
DEBUG_WARNING(true, ("Creature %s has a mod flagged as "
"trigger when done, but has no mod name.",
getNetworkId().getValueString().c_str()));
}
if (newMod.mod.flags & AttribMod::AMF_visible)
{
newMod.mod.flags &= ~AttribMod::AMF_visible;
DEBUG_WARNING(true, ("Creature %s received a mod flagged as "
"visible, but has no mod name.",
getNetworkId().getValueString().c_str()));
}
}
else
{
m_attributeModList.insert(mod.tag, newMod);
}
if ((mod.flags & AttribMod::AMF_visible) && isPlayerControlled())
{
// send the mod name and the mod time length to the player
// NOTE: we just send the tag value to the client, we expect
// the client to get the mod name from a datatable
float time = mod.attack;
if (mod.decay > 0)
time += mod.decay;
if (mod.sustain > 0)
time += mod.sustain;
else if (mod.sustain == -1.0f)
time = -1.0f;
sendTimedModData(mod.tag, time);
}
}
else
{
const char * modName = AttribModNameManager::getInstance().getAttribModName(mod.tag);
if (modName == NULL)
modName = "<NULL>";
WARNING(true, ("Creature %s received a mod %s with invalid "
"attack(%.2f) or duration(%.2f)",
getNetworkId().getValueString().c_str(), modName, mod.attack,
mod.sustain));
}
}
--m_deferComputeTotalAttributes;
}
else
{
sendControllerMessageToAuthServer(CM_addAttributeModifier,
new MessageQueueGenericValueType<AttribMod::AttribMod>(mod));
}
return damage;
} // CreatureObject::addAttributeModifier
//-----------------------------------------------------------------------
/**
* Checks if the creature has a given attrib/skill mod on them.
*
* @param modName the name of the mod to look for
*
* @return tue if the creature has the mod, false if not
*/
bool CreatureObject::hasAttribModifier(const std::string & modName) const
{
// since the mod name may be a group name, we need to get any names
// that may be derived from the modName
std::vector<uint32> nameCrcs;
AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs);
// if we have any name that matches a value in nameCrcs, we can return true
for (std::vector<uint32>::const_iterator i = nameCrcs.begin();
i != nameCrcs.end(); ++i)
{
if (m_attributeModList.find(*i) != m_attributeModList.end())
return true;
}
return false;
} // CreatureObject::hasAttribModifier
//-----------------------------------------------------------------------
/**
* Removes an attribute/skill modifier from this creature.
*
* @param modName the name of the mod to look for
*/
void CreatureObject::removeAttributeModifier (const std::string & modName)
{
// since the mod name may be a group name, we need to get any names
// that may be derived from the modName
std::vector<uint32> nameCrcs;
AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs);
// if we have any name that matches a value in nameCrcs, we can return true
std::map<uint32, CreatureMod>::const_iterator f;
for (std::vector<uint32>::const_iterator i = nameCrcs.begin();
i != nameCrcs.end(); ++i)
{
f = m_attributeModList.find(*i);
if (f != m_attributeModList.end())
{
const CreatureMod & m = (*f).second;
// if the mod is visible, we need to tell the player it's being
// removed
if (m.mod.flags & AttribMod::AMF_visible)
{
sendCancelTimedMod(m.mod.tag);
}
const char * type = "attrib";
if (AttribMod::isSkillMod(m.mod))
{
const char * skillModName = AttribModNameManager::getInstance().getAttribModName(m.mod.skill);
if (skillModName != NULL)
addModValue(skillModName, -m.maxVal, true);
else
{
WARNING(true, ("Unable to remove skillmod mod on player %s due to "
"unknown skill mod crc. Mod data: %u %u %d %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.skill, m.mod.value, m.mod.sustain, m.mod.flags));
}
type = "skill";
}
m_attributeModList.erase(f);
// LOG("CustomerService", ("Attribs: Removed named %s mod %s from %s",
// type, modName.c_str(), PlayerObject::getAccountDescription(this).c_str()));
}
}
} // CreatureObject::removeAttributeModifier
//-----------------------------------------------------------------------
/**
* Removes all attribute modifiers for a given attribute from this creature.
*
* @param attribute the attribute to clear
*/
void CreatureObject::removeAttributeModifiers(Attributes::Enumerator attribute)
{
if(isAuthoritative())
{
DEBUG_FATAL(attribute < 0 || attribute >= Attributes::NumberOfAttributes,
("attribute out of range\n"));
LOG("CustomerService", ("Attribs: Removed all attrib mods affecting "
"attrib %d from %s", attribute,
PlayerObject::getAccountDescription(this).c_str()));
m_cachedCurrentAttributeModValues.set(attribute, 0);
m_cachedMaxAttributeModValues.set(attribute, 0);
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end();)
{
if (!AttribMod::isSkillMod((*i).second.mod) &&
(*i).second.mod.attrib == attribute)
{
// if the mod is visible, we need to tell the player it's being
// removed
if ((*i).second.mod.flags & AttribMod::AMF_visible)
{
sendCancelTimedMod((*i).second.mod.tag);
}
i = m_attributeModList.erase(i);
}
else
++i;
}
computeTotalAttributes();
}
else
{
sendControllerMessageToAuthServer(CM_removeAttributeModifiers, new MessageQueueGenericValueType<Attributes::Enumerator>(attribute));
}
} // CreatureObject::removeAttributeModifiers
//-----------------------------------------------------------------------
/**
* Removes all the attribute modifiers from this creature.
*/
void CreatureObject::removeAllAttributeModifiers ()
{
int i;
if (isAuthoritative())
{
for (i = 0; i < Attributes::NumberOfAttributes; ++i)
{
m_cachedCurrentAttributeModValues.set(i, 0);
m_cachedMaxAttributeModValues.set(i, 0);
}
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end();)
{
if (!AttribMod::isSkillMod((*i).second.mod))
{
// if the mod is visible, we need to tell the player it's being
// removed
if ((*i).second.mod.flags & AttribMod::AMF_visible)
{
sendCancelTimedMod((*i).second.mod.tag);
}
i = m_attributeModList.erase(i);
}
else
++i;
}
computeTotalAttributes();
LOG("CustomerService", ("Attribs: removed all attrib mods from %s.",
PlayerObject::getAccountDescription(this).c_str()));
}
else
{
sendControllerMessageToAuthServer(CM_removeAllAttributeModifiers, 0);
}
} // CreatureObject::removeAllAttributeModifiers
//-----------------------------------------------------------------------
/**
* Removes all the attribute and skill mod modifiers from this creature.
* Does NOT remove buffs, only the effects so be sure to manually remove
* any buffs for which you need the icons to go away.
*/
void CreatureObject::removeAllAttributeAndSkillmodMods ()
{
if (isAuthoritative())
{
// remove all attrib and skill mod mods
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end();)
{
const CreatureMod & m = (*i).second;
if (AttribMod::isSkillMod(m.mod))
{
const char * skillModName = AttribModNameManager::getInstance().getAttribModName(m.mod.skill);
if (skillModName != NULL)
addModValue(skillModName, -m.maxVal, true);
}
// if the mod is visible, we need to tell the player it's being
// removed
if (m.mod.flags & AttribMod::AMF_visible)
{
sendCancelTimedMod(m.mod.tag);
}
// if the mod is flagged as having a callback, call the script
if (m.mod.flags & AttribMod::AMF_triggerOnDone)
{
const char * modName = AttribModNameManager::getInstance().getAttribModName(m.mod.tag);
if (modName != NULL)
{
ScriptParams params;
params.addParam(modName);
params.addParam(true);
if (AttribMod::isAttribMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_ATTRIB_MOD_DONE, params));
}
else if (AttribMod::isSkillMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_SKILL_MOD_DONE, params));
}
}
}
i = m_attributeModList.erase(i);
}
for (int j = 0; j < Attributes::NumberOfAttributes; ++j)
{
m_cachedCurrentAttributeModValues.set(j, 0);
m_cachedMaxAttributeModValues.set(j, 0);
}
computeTotalAttributes();
}
else
{
sendControllerMessageToAuthServer(CM_removeAllAttributeAndSkillmodModifiers, 0);
}
}
//-----------------------------------------------------------------------
/**
* Returns a attrib/skillmod mod with a given name attached to the creature.
*
* @param modName the mod to look for
*
* @return the mod, or NULL if there is no mod with that name attached to us
*/
const AttribMod::AttribMod * CreatureObject::getAttributeModifier(
const std::string & modName) const
{
// since the mod name may be a group name, we need to get any names
// that may be derived from the modName
std::vector<uint32> nameCrcs;
AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs);
// if we have any name that matches a value in nameCrcs, we can return it
for (std::vector<uint32>::const_iterator i = nameCrcs.begin();
i != nameCrcs.end(); ++i)
{
std::map<uint32, CreatureMod>::const_iterator found = m_attributeModList.find(*i);
if (found != m_attributeModList.end())
return &((*found).second.mod);
}
return NULL;
} // CreatureObject::getAttributeModifier
//-----------------------------------------------------------------------
/**
* Returns the attribute modifiers on this creature.
*/
const std::map<uint32, CreatureMod> & CreatureObject::getAttributeModifiers() const
{
return m_attributeModList.getMap();
} // CreatureObject::getAttributeModifiers
//-----------------------------------------------------------------------
/**
* Sends info about a mod that's affecting a player to the client.
*
* @param id the mod id
* @param time how long the mod lasts (in sec)
* @param updateCache whether or not to update the cached list of timed mods that have been sent to the client
*/
void CreatureObject::sendTimedModData(uint32 id, float time, bool updateCache/* = true*/)
{
if (isPlayerControlled() && getController() != NULL && id != 0)
{
MessageQueueGenericValueType<std::pair<uint32, float> > * const msg =
new MessageQueueGenericValueType<std::pair<uint32, float> >(
std::make_pair(id, time));
getController()->appendMessage(
static_cast<int>(CM_modData),
0,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
if (isPlayerControlled() && isAuthoritative() && updateCache && id != 0)
{
const int index = m_timedMod.find(id);
if (index >= 0)
{
m_timedModDuration.set(index, time);
m_timedModUpdateTime.set(index, ServerClock::getInstance().getGameTimeSeconds());
}
else
{
m_timedMod.push_back(id);
m_timedModDuration.push_back(time);
m_timedModUpdateTime.push_back(ServerClock::getInstance().getGameTimeSeconds());
}
}
} // CreatureObject::sendTimedModData
//-----------------------------------------------------------------------
/**
* Tells a client to cancel a timed mod.
*
* @param id the mod id
*/
void CreatureObject::sendCancelTimedMod(uint32 id)
{
if (isPlayerControlled() && getController() != NULL && id != 0)
{
MessageQueueGenericValueType<uint32> * const msg =
new MessageQueueGenericValueType<uint32>(id);
getController()->appendMessage(
static_cast<int>(CM_cancelMod),
0,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
if (isPlayerControlled() && isAuthoritative() && id != 0)
{
const int index = m_timedMod.find(id);
if (index >= 0)
{
m_timedMod.erase(index);
m_timedModDuration.erase(index);
m_timedModUpdateTime.erase(index);
}
}
} // CreatureObject::sendCancelTimedMod
//-----------------------------------------------------------------------
/**
* Applies damage done during combat to the creature.
*
* @param damageData the damage
*/
void CreatureObject::applyDamage(const CombatEngineData::DamageData &damageData)
{
WARNING_STRICT_FATAL(!isAuthoritative(), ("CreatureObject::applyDamageTo "
"invoked on non-authoritative object %s:%s", getObjectTemplateName(),
getNetworkId().getValueString().c_str()));
if (!isAuthoritative())
return;
//-- vehicles get damaged like tangibles
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
TangibleObject::applyDamage (damageData);
return;
}
int healthDamage = 0; // damage info we will send to the client
int actionDamage = 0; // damage info we will send to the client
int mindDamage = 0; // damage info we will send to the client
ServerObject *attacker = safe_cast<ServerObject *>(damageData.attackerId.getObject());
// if the attacker is a player and we are not, and we are incapped/dead,
// don't allow additional damage
if (attacker != NULL && attacker->isPlayerControlled() && !isPlayerControlled() &&
(isIncapacitated() || isDead()))
{
return;
}
++m_deferComputeTotalAttributes;
std::vector<AttribMod::AttribMod>::const_iterator iter;
for (iter = damageData.damage.begin(); iter != damageData.damage.end(); ++iter)
{
const AttribMod::AttribMod &mod = *iter;
if (mod.value == 0)
continue;
int damage = -addAttributeModifier(mod, attacker);
if (damage != 0)
{
if (mod.attrib == Attributes::Health)
healthDamage += damage;
else if (mod.attrib == Attributes::Action)
actionDamage += damage;
else if (mod.attrib == Attributes::Mind)
mindDamage += damage;
}
}
--m_deferComputeTotalAttributes;
computeTotalAttributes();
// tell scripts we have taken combat damage
if (damageData.attackerId != NetworkId::cms_invalid &&
(healthDamage > 0 || actionDamage > 0 || mindDamage > 0))
{
NOT_NULL(getScriptObject());
int damageArray[] = {healthDamage, 0, actionDamage, 0, mindDamage, 0};
std::vector<int> damageParam(&damageArray[0], &damageArray[Attributes::NumberOfAttributes]);
ScriptParams params;
params.addParam(damageData.attackerId);
params.addParam(damageData.weaponId);
params.addParam(damageParam);
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_CREATURE_DAMAGED,
params));
}
testIncapacitation(damageData.attackerId);
} // CreatureObject::applyDamage
//-----------------------------------------------------------------------
/**
* Changes attrib mod(s) on the creature due to combat damage or healing. Note
* that only positive attrib mods can be changed
*
* @param attrib the index of the attribute getting damaged
* @param delta the amount to change the mod by
* @param checkPoolOverflow flag to check if a pool value is over its max value
*
* @return the amount the attrib mod(s) changed
*/
int CreatureObject::changeAttribModCurrentValue(Attributes::Enumerator attrib, int delta, bool checkPoolOverflow)
{
if (!isAuthoritative())
{
DEBUG_WARNING(true, ("CreatureObject::damageAttribMods called for "
"non-authoritative creature %s, attrib %d",
getNetworkId().getValueString().c_str(), attrib));
return 0;
}
if (attrib < 0 || attrib >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attrib));
return 0;
}
int startDelta = delta;
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end() &&
delta != 0; ++i)
{
CreatureMod m = (*i).second;
if (AttribMod::isAttribMod(m.mod) && m.mod.attrib == attrib && m.mod.value > 0)
{
if (delta > 0)
{
// healing mods
if (m.maxVal > m.currentVal)
{
if (m.maxVal - m.currentVal >= delta)
{
// this mod can take the entire change
m.currentVal += delta;
delta = 0;
}
else
{
// this mod can only take part of the change
delta -= (m.maxVal - m.currentVal);
m.currentVal = m.maxVal;
}
m_attributeModList.set((*i).first, m);
}
}
else
{
// damaging mods
if (m.currentVal > 0)
{
if (m.currentVal + delta > 0)
{
// this mod can take the entire change
m.currentVal += delta;
delta = 0;
}
else
{
// this mod can only take part of the change
delta += m.currentVal;
m.currentVal = 0;
}
m_attributeModList.set((*i).first, m);
}
}
}
}
if (startDelta != delta)
recomputeAttribModTotals(checkPoolOverflow);
return startDelta - delta;
} // CreatureObject::changeAttribModCurrentValue
//-----------------------------------------------------------------------
/**
* Recomputes the values due to attrib mods to be added to the creature's current
* and max attribs.
*
* @param checkPoolOverflow flag to check if a pool value is over its max value
*/
void CreatureObject::recomputeAttribModTotals(bool checkPoolOverflow)
{
int i;
if (!isAuthoritative())
return;
int newCachedMaxAttributeModValues[Attributes::NumberOfAttributes];
int newCachedCurrentAttributeModValues[Attributes::NumberOfAttributes];
for (i = 0; i < Attributes::NumberOfAttributes; ++i)
{
newCachedMaxAttributeModValues[i] = 0;
newCachedCurrentAttributeModValues[i] = 0;
}
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter j = m_attributeModList.begin(); j != m_attributeModList.end(); ++j)
{
const CreatureMod & m = (*j).second;
if (AttribMod::isAttribMod(m.mod))
{
if ((m.mod.flags & AttribMod::AMF_changeMax))
newCachedMaxAttributeModValues[m.mod.attrib] += m.maxVal;
newCachedCurrentAttributeModValues[m.mod.attrib] += m.currentVal;
}
}
for (i = 0; i < Attributes::NumberOfAttributes; ++i)
{
m_cachedMaxAttributeModValues.set(i, static_cast<Attributes::Value>(newCachedMaxAttributeModValues[i]));
m_cachedCurrentAttributeModValues.set(i, static_cast<Attributes::Value>(newCachedCurrentAttributeModValues[i]));
if (newCachedCurrentAttributeModValues[i] < 0)
{
// don't let the mod prevent us from healing out of incapacitation
int healedAttrib = getAdjustedAttribute(i, getUnmodifiedMaxAttribute(i));
if (healedAttrib < 0)
{
m_cachedMaxAttributeModValues.set(i, static_cast<Attributes::Value>(
m_cachedMaxAttributeModValues[i] - healedAttrib + 1));
m_cachedCurrentAttributeModValues.set(i, static_cast<Attributes::Value>(
m_cachedCurrentAttributeModValues[i] - healedAttrib + 1));
}
}
if (Attributes::isAttribPool(i) && checkPoolOverflow)
{
// since we can modify the max value and not the current value, damage
// the attribute if it is over the max
int max = getMaxAttribute(i);
int current = getUnmodifiedAttribute(i) + m_cachedCurrentAttributeModValues[i];
if (current > max)
{
IGNORE_RETURN(alterAttribute(i, max - current, true));
}
}
}
} // CreatureObject::recomputeAttribModTotals
//-----------------------------------------------------------------------
/**
* Advances the timing of attribute buffs and debuffs. Should be called by the controller from the
* authoritative object.
*
* @param time The amount of time to advance the buff.
*/
void CreatureObject::decayAttributes(float time)
{
if (!isAuthoritative())
return;
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::decayAttributes");
// prevent client-seen attributes from being updated until all the attributes
// have been modified
++m_deferComputeTotalAttributes;
const uint32 currentTime = ServerClock::getInstance().getGameTimeSeconds();
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
{ // contain i because VC isn't compilant
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end();)
{
CreatureMod m = (*i).second;
if (AttribMod::isDirectDamage(m.mod))
{
DEBUG_WARNING(true, ("CreatureObject::decayAttributes has direct "
"damage mod in its list"));
continue;
}
const float deltaTime = static_cast<float>(currentTime - m.startTime);
float decayBegin = m.mod.attack;
if (m.mod.sustain > 0)
decayBegin += m.mod.sustain;
float totalTime = decayBegin;
if (m.mod.decay > 0)
totalTime += m.mod.decay;
if (m.mod.sustain == -1.0f)
{
// mod never expires
decayBegin = -1.0f;
totalTime = -1.0f;
}
int modValue = 0;
if (deltaTime >= totalTime && totalTime != -1.0f)
{
// end of the mod time, erase the mod
if (AttribMod::isSkillMod(m.mod))
{
const char * skillModName = AttribModNameManager::getInstance().getAttribModName(m.mod.skill);
if (skillModName != NULL)
{
addModValue(skillModName, -m.maxVal, true);
LOG("CustomerService", ("Attribs: Expired skill mod on %s. Mod data: "
"%u %u %d %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.skill, m.mod.value, m.mod.sustain, m.mod.flags));
}
else
{
WARNING(true, ("Unable to expire skillmod mod on player %s due to "
"unknown skill mod crc. Mod data: %u %u %d %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.skill, m.mod.value, m.mod.sustain, m.mod.flags));
}
}
else
{
LOG("CustomerService", ("Attribs: Expired attrib mod on %s. Mod data: "
"%u %d %d %.2f %.2f %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.attrib, m.mod.value, m.mod.attack, m.mod.sustain,
m.mod.decay, m.mod.flags));
}
// remove the mod from ourself
i = m_attributeModList.erase(i);
if (m.mod.flags & AttribMod::AMF_triggerOnDone)
{
// tell scripts the mod has ended
const char * modName = AttribModNameManager::getInstance().getAttribModName(m.mod.tag);
if (modName != NULL)
{
ScriptParams params;
params.addParam(modName);
params.addParam(false);
if (AttribMod::isAttribMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_ATTRIB_MOD_DONE, params));
}
else if (AttribMod::isSkillMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_SKILL_MOD_DONE, params));
}
}
else
{
WARNING(true, ("Unable to trigger mod done on player %s due to "
"unknown mod name crc. Mod data: %u %u %d %.2f %.2f %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.attrib, m.mod.value, m.mod.attack,
m.mod.sustain, m.mod.decay, m.mod.flags));
}
}
if (m.mod.flags & AttribMod::AMF_visible)
{
// tell the client to erase the mod icon
sendCancelTimedMod(m.mod.tag);
}
}
else
{
if (deltaTime < m.mod.attack) // in attack phase
{
float t = deltaTime / m.mod.attack;
modValue = static_cast<int>(m.mod.value * t + 0.5f);
}
else if (deltaTime < decayBegin || decayBegin == -1.0f) // in sustain phase
{
modValue = m.mod.value;
}
else if (deltaTime < totalTime) // in decay phase
{
float t = (deltaTime - decayBegin) / m.mod.decay;
modValue = static_cast<int>(m.mod.value * (1.0f-t) + 0.5f);
}
if (m.maxVal != modValue)
{
int delta = modValue - m.maxVal;
// Even if the mod is flagged as not updating max, we still need
// to change it here so we can calculate the next frame's delta.
// We'll check for updating max in recomputeAttribModTotals instead.
m.maxVal = modValue;
if (AttribMod::isSkillMod(m.mod))
{
// add the skillmod mod as if it were from a skill,
// which makes it temporary
const char * skillModName = AttribModNameManager::getInstance().getAttribModName(m.mod.skill);
if (skillModName != NULL)
addModValue(skillModName, delta, true);
else
{
WARNING(true, ("Unable to add skillmod mod on player %s due to "
"unknown skill mod crc. Mod data: %u %u %d %.2f %d",
PlayerObject::getAccountDescription(this).c_str(),
m.mod.tag, m.mod.skill, m.mod.value, m.mod.sustain, m.mod.flags));
}
}
else
{
// see if we need to update the mod current value in addition
// to the max value
// non-pool attribs must update the current value
bool updateCurrent = !Attributes::isAttribPool(m.mod.attrib);
if (!updateCurrent)
{
// check the mod flags
if (deltaTime < decayBegin)
{
if ((m.mod.flags & AttribMod::AMF_attackCurrent))
updateCurrent = true;
}
else
{
if ((m.mod.flags & AttribMod::AMF_decayCurrent))
updateCurrent = true;
}
}
if (updateCurrent)
{
if ((m.mod.flags & AttribMod::AMF_changeMax) ||
!Attributes::isAttribPool(m.mod.attrib))
{
m.currentVal += delta;
}
else
{
// we are doing damage/healing over time, modify the
// attribute directly
IGNORE_RETURN(alterAttribute(m.mod.attrib, delta, true,
NetworkId::cms_invalid, false));
}
}
}
// make sure the mod's current val doesn't get out of range
if (m.mod.value >= 0)
{
if (m.currentVal > m.maxVal)
m.currentVal = m.maxVal;
else if (m.currentVal < 0)
m.currentVal = 0;
}
else
{
if (m.currentVal < m.maxVal)
m.currentVal = m.maxVal;
else if (m.currentVal > 0)
m.currentVal = 0;
}
m_attributeModList.set((*i).first, m);
}
++i;
}
}
}
recomputeAttribModTotals(true);
// regenerate attributes
int i;
bool attribRegen = false;
float regenerationRate[3] = {0,0,0};
m_regenerationTime += time;
for (i = 0; i < 3; ++i)
{
int poolAttrib = Attributes::POOLS[i];
int maxAttrib = getMaxAttribute(poolAttrib);
int currentAttrib = getAttribute(poolAttrib);
if (currentAttrib < maxAttrib)
{
attribRegen = true;
regenerationRate[i] = getRegenRate(poolAttrib);
m_regeneration[poolAttrib] += regenerationRate[i] * time;
}
}
//--
//-- update player incapacitation timer
//--
if (isPlayerControlled())
{
// regenerate force powers
if (!isIncapacitated())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
playerObject->regenerateForcePower(time);
}
}
}
// update the client
--m_deferComputeTotalAttributes;
computeTotalAttributes();
if (isAuthoritative())
testIncapacitation(NetworkId::cms_invalid);
}
//-----------------------------------------------------------------------
/**
* Advances the timing of mental state buffs and debuffs and decays mental states toward
* their default values. Should be called by the controller from the
* authoritative object.
*
* @param time The amount of time to advance the buff.
*/
void CreatureObject::decayMentalStates(float time)
{
if (isDead())
return;
// @todo AJS - make this take into account time since objects were unloaded when they are reloaded
int oldBehavior = m_currentAttitude.get().m_behavior;
CreatureAttitude newState = m_currentAttitude.get();
updateBehavior(0, newState);
CreatureAttitude newTargetState;
int i;
// Initialize the decay values for each mental state
float decays[MentalStates::NumberOfMentalStates];
for (i=0; i<MentalStates::NumberOfMentalStates; ++i)
{
// decay the values
const float decay = time * 100.0f / m_mentalStateDecays[i];
decays[i] = decay;
}
// decay the main mental states
for (i=0; i<MentalStates::NumberOfMentalStates; ++i)
{
MentalStates::Value newValue = newState.m_currentValues[i];
newValue = std::max(0.0f, newValue - decays[i]);
newState.m_currentValues[i] = newValue;
}
// decay targetted mental states
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator targetIterator, targetEnd = m_mentalStatesToward.end();
for (targetIterator=m_mentalStatesToward.begin(); targetIterator != targetEnd; ++targetIterator)
{
newTargetState = targetIterator->second;
for (i=0; i<MentalStates::NumberOfMentalStates; ++i)
{
MentalStates::Value newValue = targetIterator->second.m_currentValues[i];
if (newValue > 0)
{
newValue = std::max(newValue - decays[i], 0.0f);
}
else
{
newValue = std::min(newValue + decays[i], 0.0f);
}
newTargetState.m_currentValues[i] = newValue;
}
m_mentalStatesToward.set(targetIterator->first, newTargetState);
}
// Now change the behaviors for all of the targetted objects
bool behaviorChanged[Behaviors::NumberOfBehaviors];
int behavior;
// Clear the accumulated values for each behavior
for (behavior = 0; behavior < Behaviors::NumberOfBehaviors; ++behavior)
{
behaviorChanged[behavior] = false;
}
clipMentalStatesToward();
deleteUnusedMentalStatesToward(behaviorChanged);
int newBehavior = newState.m_behavior;
if (!m_mentalStatesToward.empty())
{
// Update the behavior toward each target, and also update the accumulated state values for each behavior
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator i = m_mentalStatesToward.begin(), e=m_mentalStatesToward.end();
for (; i != e; ++i)
{
oldBehavior = i->second.m_behavior;
newTargetState = i->second;
updateBehavior(&i->first, newTargetState);
if (newTargetState.m_behavior != oldBehavior)
{
if (oldBehavior >= 0)
{
m_targetsByBehavior[oldBehavior].erase(i->first);
behaviorChanged[oldBehavior] = true;
}
m_targetsByBehavior[newTargetState.m_behavior].insert(i->first);
behaviorChanged[newTargetState.m_behavior] = true;
}
m_mentalStatesToward.set(i->first, newTargetState);
if (newTargetState.m_behavior > newBehavior)
newBehavior = newTargetState.m_behavior;
}
}
m_currentAttitude.set(newState);
if (m_lastBehavior.get() != newBehavior)
{
const int lastBehavior = m_lastBehavior.get();
m_lastBehavior = newBehavior;
behaviorChanged[newBehavior] = false;
notifyTargetChange(lastBehavior, newBehavior, behaviorChanged);
}
else
{
notifyTargetChange(m_lastBehavior.get(), m_lastBehavior.get(), behaviorChanged);
}
}
// --------------------------------------------------------------------------
/**
* Returns the weapon currently being carried by the creature, which may or may
* not be the weapon it is using. If the creature is not carrying a weapon,
* returns the default weapon.
*
* @return the weapon
*/
WeaponObject *CreatureObject::getReadiedWeapon() const
{
static SlotId const rightHandSlot = SlotIdManager::findSlotId(ConstCharCrcLowerString("hold_r"));
// first check the right hand.
SlottedContainer const * const slottedContainer = ContainerInterface::getSlottedContainer(*this);
if (slottedContainer)
{
if (slottedContainer->hasSlot(rightHandSlot))
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
Object * const o = slottedContainer->getObjectInSlot(rightHandSlot, tmp).getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
{
WeaponObject * const wo = so->asWeaponObject();
if (wo)
return wo;
}
}
}
}
// nothing in the hand, return the default weapon
return getDefaultWeapon();
}
//----------------------------------------------------------------------
/**
* Returned the weapon the creature will use for combat.
*
* @return the weapon
*/
WeaponObject *CreatureObject::getCurrentWeapon()
{
Object * const o = m_currentWeapon.get().getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
{
WeaponObject * const currentWeapon = so->asWeaponObject();
if (currentWeapon)
return currentWeapon;
}
}
WeaponObject * const readiedWeapon = getReadiedWeapon();
if (readiedWeapon)
setCurrentWeapon(*readiedWeapon);
return readiedWeapon;
}
//----------------------------------------------------------------------
/**
* Sets the weapon the creature will use for combat.
*
* @param weapon the weapon
*/
void CreatureObject::setCurrentWeapon(WeaponObject & weapon)
{
if(isAuthoritative())
{
m_currentWeapon = CachedNetworkId(weapon);
}
else
{
sendControllerMessageToAuthServer(CM_setCurrentWeapon, new MessageQueueGenericValueType<NetworkId>(weapon.getNetworkId()));
};
}
// --------------------------------------------------------------------------
int CreatureObject::getInstrumentVisualId() const
{
static SlotId rightHandSlot = SlotIdManager::findSlotId(ConstCharCrcLowerString("hold_r"));
bool hasWeapon = false;
// first check the right hand.
SlottedContainer const * const slottedContainer = ContainerInterface::getSlottedContainer(*this);
if (slottedContainer)
{
if (slottedContainer->hasSlot(rightHandSlot))
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
Object const * const object = slottedContainer->getObjectInSlot(rightHandSlot, tmp).getObject();
if (object)
{
int type = lookupInstrumentVisualId(object->getObjectTemplateName());
if (type)
return type;
ServerObject const * const so = object->asServerObject();
if (so && so->asWeaponObject())
hasWeapon = true;
}
}
}
// then check the intended target if no weapon is equipped
if(!hasWeapon)
{
int visualId = internalGetInstrumentVisualId(getIntendedTarget());
if(visualId)
return visualId;
}
// last, check the look at target if no weapon is equipped
if (!hasWeapon)
{
int visualId = internalGetInstrumentVisualId(getLookAtTarget());
if(visualId)
return visualId;
}
return 0;
}
// --------------------------------------------------------------------------
int CreatureObject::getInstrumentAudioId() const
{
static SlotId rightHandSlot = SlotIdManager::findSlotId(ConstCharCrcLowerString("hold_r"));
bool hasWeapon = false;
// first check the right hand.
SlottedContainer const * const slottedContainer = ContainerInterface::getSlottedContainer(*this);
if (slottedContainer)
{
if (slottedContainer->hasSlot(rightHandSlot))
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
Object const * const object = slottedContainer->getObjectInSlot(rightHandSlot, tmp).getObject();
if (object)
{
int type = lookupInstrumentAudioId(object->getObjectTemplateName());
if (type)
return type;
ServerObject const * const so = object->asServerObject();
if (so && so->asWeaponObject())
hasWeapon = true;
}
}
}
// then check the intended target if no weapon is equipped
if (!hasWeapon)
{
int audioId = internalGetInstrumentAudioId(getIntendedTarget());
if(audioId)
return audioId;
}
// last, check the look at target if no weapon is equipped
if (!hasWeapon)
{
int audioId = internalGetInstrumentAudioId(getLookAtTarget());
if(audioId)
return audioId;
}
return 0;
}
// --------------------------------------------------------------------------
int CreatureObject::internalGetInstrumentAudioId(NetworkId const &id) const
{
if (id != NetworkId::cms_invalid)
{
Object const * const object = NetworkIdManager::getObjectById(id);
if ( object
&& !instrumentTooFar(getPosition_w(), object->getPosition_w())
&& ContainerInterface::getContainedByObject(*this) == ContainerInterface::getContainedByObject(*object))
{
ServerObject const * const serverObj = object->asServerObject();
if (!serverObj->getSharedTemplate()->getArrangementDescriptor())
{
int type = lookupInstrumentAudioId(object->getObjectTemplateName());
if (type)
return type;
}
}
}
return 0;
}
// --------------------------------------------------------------------------
int CreatureObject::internalGetInstrumentVisualId(NetworkId const &id) const
{
if (id != NetworkId::cms_invalid)
{
Object const * const object = NetworkIdManager::getObjectById(id);
if ( object
&& !instrumentTooFar(getPosition_w(), object->getPosition_w())
&& ContainerInterface::getContainedByObject(*this) == ContainerInterface::getContainedByObject(*object))
{
ServerObject const * const serverObj = object->asServerObject();
if (!serverObj->getSharedTemplate()->getArrangementDescriptor())
{
int type = lookupInstrumentVisualId(object->getObjectTemplateName());
if (type)
return type;
}
}
}
return 0;
}
// --------------------------------------------------------------------------
GroupObject *CreatureObject::getGroup() const
{
if (m_group.get() != NetworkId::cms_invalid)
{
Object *obj = m_group.get().getObject();
if (obj)
{
ServerObject *serverObj = obj->asServerObject();
return serverObj->asGroupObject();
}
}
return 0;
}
// --------------------------------------------------------------------------
void CreatureObject::setGroup(GroupObject *group, bool disbandingCurrentGroup)
{
if (isAuthoritative())
{
NetworkId const oldGroupId = m_group.get();
m_group = group ? CachedNetworkId(*group) : CachedNetworkId::cms_cachedInvalid;
recomputeSlopeModPercent();
// Our group has finally been established, so don't worry about
// keeping track of who invited us to the group
setInviterForPendingGroup( NetworkId::cms_invalid );
setGroupInviter(NetworkId::cms_invalid, std::string(), NetworkId::cms_invalid);
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject && (playerObject->getSessionStartPlayTime() != 0))
{
std::map<NetworkId, LfgCharacterData> const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData();
std::map<NetworkId, LfgCharacterData>::const_iterator iterFind = connectedCharacterLfgData.find(getNetworkId());
if ((iterFind != connectedCharacterLfgData.end()) && (iterFind->second.groupId != m_group.get()))
ServerUniverse::setConnectedCharacterGroupData(getNetworkId(), m_group.get());
}
// if joining a group, and the group currently
// has an active group pickup point, handle it
if (group && (oldGroupId != group->getNetworkId()))
{
unsigned int const secondsLeftOnGroupPickup = group->getSecondsLeftOnGroupPickup();
if (secondsLeftOnGroupPickup)
{
std::pair<std::string, Vector> const & groupPickupLocation = group->getGroupPickupLocation();
if (!groupPickupLocation.first.empty())
{
// tell group member that there's an active group pickup point
if (getClient())
{
StringId::LocUnicodeString response;
if (StringId("group", "create_group_pickup_success_new_group_member").localize(response))
{
ConsoleMgr::broadcastString(FormattedString<2048>().sprintf(Unicode::wideToNarrow(response).c_str(), CalendarTime::convertSecondsToMS(secondsLeftOnGroupPickup).c_str()),
getClient());
}
}
// create/update the group member's group pickup point waypoint
if (playerObject)
{
Location const location(groupPickupLocation.second, NetworkId::cms_invalid, Location::getCrcBySceneName(groupPickupLocation.first));
playerObject->createOrUpdateReusableWaypoint(location, "groupPickupWp", Unicode::narrowToWide("Group Pickup Point"), Waypoint::White);
}
}
}
}
}
else
{
NetworkId groupId;
if(group)
groupId = group->getNetworkId();
sendControllerMessageToAuthServer(CM_setGroup, new MessageQueueGenericValueType<std::pair<bool, NetworkId> >(std::make_pair(disbandingCurrentGroup, groupId)));
}
}
// --------------------------------------------------------------------------
NetworkId const & CreatureObject::getGroupInviterId() const
{
return m_groupInviter.get().first.first;
}
// --------------------------------------------------------------------------
std::string const & CreatureObject::getGroupInviterName() const
{
return m_groupInviter.get().first.second;
}
// --------------------------------------------------------------------------
CreatureObject *CreatureObject::getGroupInviter() const
{
NetworkId const & inviterId = m_groupInviter.get().first.first;
if (inviterId != NetworkId::cms_invalid)
{
Object * const o = NetworkIdManager::getObjectById(inviterId);
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
return so->asCreatureObject();
}
}
return 0;
}
// --------------------------------------------------------------------------
void CreatureObject::setGroupInviter(NetworkId const &inviterId, std::string const& inviterName, NetworkId const &inviterShipId)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<std::pair<std::string, std::pair<NetworkId, NetworkId> > > *msg = new MessageQueueGenericValueType<std::pair<std::string, std::pair<NetworkId, NetworkId> > >(std::make_pair(inviterName, std::make_pair(inviterId, inviterShipId)));
controller->appendMessage(
CM_setGroupInviter,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
m_groupInviter = PlayerAndShipPair(std::make_pair(inviterId, inviterName), inviterShipId);
}
}
// --------------------------------------------------------------------------
NetworkId const &CreatureObject::getInviterForPendingGroup() const
{
// We agreed to join a group and this is the guy that invited us
return m_inviterForPendingGroup.get();
}
// --------------------------------------------------------------------------
void CreatureObject::setInviterForPendingGroup(NetworkId const &inviterId)
{
// Once we agree to join a group, we keep track of who extended
// the invitation until we get an actual group object
m_inviterForPendingGroup = inviterId;
}
// --------------------------------------------------------------------------
void CreatureObject::notifyTargetChange(int lastBehavior, int newBehavior, bool * i_targetsChanged)
{
bool sendMessage = false;
Scripting::TrigId notification = Scripting::TRIG_TARGET_CHANGE;
ScriptParams params;
params.addParam(newBehavior);
if (lastBehavior != newBehavior)
{
notification = Scripting::TRIG_BEHAVIOR_CHANGE;
params.addParam(lastBehavior);
sendMessage = true;
}
std::vector<int> targetListChanged;
targetListChanged.reserve(Behaviors::NumberOfBehaviors);
targetListChanged.resize(Behaviors::NumberOfBehaviors);
int i;
for (i=0; i<Behaviors::NumberOfBehaviors; ++i)
{
targetListChanged[i]= 0;
if (i_targetsChanged[i])
{
sendMessage = true;
targetListChanged[i] = true;
}
}
if (sendMessage)
{
params.addParam(targetListChanged);
IGNORE_RETURN(getScriptObject()->trigAllScripts(notification, params));
}
}
// ----------------------------------------------------------------------
CreatureAttitude const *CreatureObject::getAttitudeToward(NetworkId const &target) const
{
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator i = m_mentalStatesToward.find(target);
if (i == m_mentalStatesToward.end())
return 0;
return &i->second;
}
// ----------------------------------------------------------------------
void CreatureObject::clipMentalStatesToward()
{
float maxDeltas[MentalStates::NumberOfMentalStates];
float minValues[MentalStates::NumberOfMentalStates];
MentalStates::Enumerator state;
for (state=0; state<MentalStates::NumberOfMentalStates; ++state)
{
MentalStates::Value current = getMentalState(state);
minValues[state] = -current;
maxDeltas[state] = m_maxMentalStates[state] - current;
}
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator i = m_mentalStatesToward.begin();
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator e = m_mentalStatesToward.end();
for (; i != e; ++i)
{
CreatureAttitude newState = i->second;
for (state=0; state<MentalStates::NumberOfMentalStates; ++state)
{
float delta = i->second.m_currentValues[state];
if (delta > maxDeltas[state])
{
newState.m_currentValues[state] = maxDeltas[state];
}
else if (delta < minValues[state])
{
newState.m_currentValues[state] = 0;
}
}
m_mentalStatesToward.set(i->first, newState);
}
}
//----------------------------------------------------------------------
void CreatureObject::deleteUnusedMentalStatesToward(bool * i_behaviorChanged)
{
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator i = m_mentalStatesToward.begin();
Archive::AutoDeltaMap<NetworkId, CreatureAttitude>::const_iterator e = m_mentalStatesToward.end();
std::vector<NetworkId> to_erase;
for (; i != e; ++i)
{
ServerObject * object = ServerWorld::findObjectByNetworkId(i->first);
bool erase = true;
int state;
if (object)
{
for (state=0; state<MentalStates::NumberOfMentalStates && erase; ++state)
{
if (i->second.m_currentValues[state] > 0)
erase = false;
}
}
if (erase)
{
int currentObjectBehavior = i->second.m_behavior;
if (currentObjectBehavior != -1) // if -1, we are deleting an object that was added and removed in the same alter
{
if (currentObjectBehavior < Behaviors::Calm || currentObjectBehavior >= Behaviors::NumberOfBehaviors)
{
DEBUG_REPORT_LOG(true, ("deleteUnusedMentalStatesToward: currentBehavior %d is invalid", currentObjectBehavior));
}
else
{
std::set<NetworkId>::iterator currentTargetByBehaviorIter = m_targetsByBehavior[currentObjectBehavior].find(i->first);
if (currentTargetByBehaviorIter != m_targetsByBehavior[currentObjectBehavior].end())
{
i_behaviorChanged[currentObjectBehavior] = true;
m_targetsByBehavior[currentObjectBehavior].erase(currentTargetByBehaviorIter);
}
}
}
to_erase.push_back(i->first);
}
}
std::vector<NetworkId>::iterator tei;
for (tei = to_erase.begin(); tei!=to_erase.end(); ++tei)
{
m_mentalStatesToward.erase(*tei);
}
}
//----------------------------------------------------------------------
void CreatureObject::setMentalStateToward(const NetworkId &target, MentalStates::Enumerator state, MentalStates::Value value)
{
if(! isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setMentalStateToward, new MessageQueueGenericValueType<std::pair<NetworkId, std::pair<MentalStates::Enumerator, MentalStates::Value> > >(std::make_pair(target, std::make_pair(state, value))));
}
else
{
if (state < 0 || state >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", state));
return;
}
ServerObject * object = ServerWorld::findObjectByNetworkId(target);
if (!object)
return;
CreatureAttitude const * const cs = getAttitudeToward(target);
CreatureAttitude newState;
if (!cs && value <= 0)
return;
if (cs)
{
newState = *cs;
}
newState.m_currentValues[state] = value;
m_mentalStatesToward.set(target, newState);
}
}
//----------------------------------------------------------------------
void CreatureObject::setMentalStateTowardClampBehavior(const NetworkId &target, MentalStates::Enumerator state, MentalStates::Value value, Behaviors::Enumerator behavior)
{
if(! isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setMentalStateTowardClampBehavior, new MessageQueueGenericValueType<std::pair<NetworkId, std::pair<MentalStates::Enumerator, std::pair<MentalStates::Value, Behaviors::Enumerator> > > >(std::make_pair(target, std::make_pair(state, std::make_pair(value, behavior)))));
}
else
{
if (state < 0 || state >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", state));
return;
}
if (behavior < Behaviors::Calm || behavior >= Behaviors::NumberOfBehaviors)
{
DEBUG_REPORT_LOG(true, ("setMentalStateTowardClampBehavior: behavior %d is invalid", behavior));
return;
}
ServerObject * object = ServerWorld::findObjectByNetworkId(target);
if (!object)
return;
CreatureAttitude const * const cs = getAttitudeToward(target);
if (!cs && value <= 0)
return;
CreatureAttitude newState;
if (cs)
{
newState = *cs;
}
float oldValue = newState.m_currentValues[state];
float maxValue = 0;
if (behavior >= Behaviors::Alert && behavior <= Behaviors::Retreat) // [1..3] 4 = flee
{
if (state != MentalStates::Fear)
{
DEBUG_REPORT_LOG(true, ("Bad mental state %d for behavior %d in setMentalStateTowardClampBehavior", state, behavior));
return;
}
maxValue = m_fearCutoffs[behavior - Behaviors::Alert + 2]; // [2..4] out of [0..4]
}
else if (behavior == Behaviors::Attack) // [5] 6 = frenzy
{
if (state != MentalStates::Anger)
{
DEBUG_REPORT_LOG(true, ("Bad mental state %d for behavior %d in setMentalStateTowardClampBehavior", state, behavior));
return;
}
maxValue = m_angerCutoffs[behavior - Behaviors::Attack + 2]; // [2..2] out of [0..2]
}
else if (behavior == Behaviors::Calm)
{
if (state == MentalStates::Fear)
{
maxValue = m_fearCutoffs[behavior+1];
}
else
{
maxValue = 0.0f;
}
}
else
{
maxValue = 1e30f; // allow any value
}
maxValue = std::max(0.0f, maxValue - 1.0f);
if (oldValue < maxValue)
{
if (value > maxValue)
value = maxValue;
newState.m_currentValues[state] = value;
m_mentalStatesToward.set(target, newState);
}
}
}
//-----------------------------------------------------------------------
/**
* Gets the creatures current state. 0 = calm, 1-4 = afraid, 5-7 = angry.
*/
bool CreatureObject::updateBehavior(const NetworkId * id, CreatureAttitude &state) const
{
int attitude = state.m_behavior;
float anger = id ? getMentalStateToward(*id, MentalStates::Anger) : getMentalState(MentalStates::Anger);
float fear = id ? getMentalStateToward(*id, MentalStates::Fear) : getMentalState(MentalStates::Fear);
while ((state.m_fearState > 0) && fear <= m_fearCutoffs[state.m_fearState-1]) // much less fearful than current state
{
state.m_fearState = static_cast<CreatureAttitude::eFearState>(state.m_fearState - 1);
}
while ((state.m_fearState < CreatureAttitude::kFearStateCount-1) && fear >= m_fearCutoffs[state.m_fearState+1])
{
state.m_fearState = static_cast<CreatureAttitude::eFearState>(state.m_fearState + 1);
}
if (anger == 0)
state.m_angerState = CreatureAttitude::kAngerCalm;
else
{
while ((state.m_angerState > CreatureAttitude::kAngerAttack) && anger <= m_angerCutoffs[state.m_angerState-1]) // much less angry than current state
{
state.m_angerState = static_cast<CreatureAttitude::eAngerState>(state.m_angerState - 1);
}
while ((state.m_angerState < CreatureAttitude::kAngerStateCount-1) && anger >= m_angerCutoffs[state.m_angerState+1])
{
state.m_angerState = static_cast<CreatureAttitude::eAngerState>(state.m_angerState + 1);
}
}
if (anger > 0)
{
if (state.m_angerState == CreatureAttitude::kAngerCalm) // got here somehow, I don't know
state.m_behavior = CreatureAttitude::kAngerAttack + CreatureAttitude::kFearStateCount-1;
else
state.m_behavior = state.m_angerState + CreatureAttitude::kFearStateCount-1;
}
else
{
if (state.m_fearState == CreatureAttitude::kFearCalm)
state.m_behavior = 0;
else
state.m_behavior = state.m_fearState;
}
return state.m_behavior != attitude;
}
//-----------------------------------------------------------------------
/**
* Returns the list of draft schematics this creature can use.
*
* @return the schematic list
*/
const std::map<std::pair<uint32, uint32>,int> & CreatureObject::getDraftSchematics() const
{
static const std::map<std::pair<uint32, uint32>,int> npcSchematics;
if (isPlayerControlled())
{
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
return playerObject->getDraftSchematics();
}
return npcSchematics;
} // CreatureObject::getDraftSchematics
//-----------------------------------------------------------------------
/**
* Tests if a given ingredient for use in crafting is in this creature's inventory.
*
* @param ingredient the ingredient to test
*
* @return true if the ingredient in in our inventory, false if not
*/
bool CreatureObject::isIngredientInInventory(const Object & ingredient) const
{
const ServerObject * inventory = getInventory();
if (inventory == NULL)
return false;
const Object * container = ContainerInterface::getContainedByObject(ingredient);
while (container != NULL)
{
if (inventory->getNetworkId() == container->getNetworkId() ||
getNetworkId() == container->getNetworkId())
{
return true;
}
// go up a container level
container = ContainerInterface::getContainedByObject(*container);
}
return false;
} // CreatureObject::isIngredientInInventory
//-----------------------------------------------------------------------
/**
* Disables schematic filtering when this creature crafts. Note this only works
* if the creature is controlled by a god-mode player.
*/
void CreatureObject::disableSchematicFiltering()
{
if (getClient() == NULL || !getClient()->isGod())
return;
setObjVarItem(OBJVAR_DISABLE_SCHEMATIC_FILTER, 1);
} // CreatureObject::disableSchematicFiltering
//-----------------------------------------------------------------------
/**
* Enables schematic filtering when this creature crafts. Note this only works
* if the creature is controlled by a god-mode player.
*/
void CreatureObject::enableSchematicFiltering()
{
if (getClient() == NULL || !getClient()->isGod())
return;
removeObjVarItem(OBJVAR_DISABLE_SCHEMATIC_FILTER);
} // CreatureObject::enableSchematicFiltering
//-----------------------------------------------------------------------
/**
* Returns if schematic filtering is enabled or not. Note this only works
* if the creature is controlled by a god-mode player.
*
* @return true if filtering is on, false if it is off
*/
bool CreatureObject::isSchematicFilteringEnabled()
{
if (getClient() == NULL || !getClient()->isGod())
return true;
return (!getObjVars().hasItem(OBJVAR_DISABLE_SCHEMATIC_FILTER));
} // CreatureObject::isSchematicFilteringEnabled
//-----------------------------------------------------------------------
/**
* Returns a list of manufacture schematics in this creature's datapad.
*
* @param schematics list to be filled with the schematics
*/
void CreatureObject::getManufactureSchematics(std::vector<const ManufactureSchematicObject *> & schematics)
{
const ServerObject * datapad = getDatapad();
if (datapad == NULL)
return;
const VolumeContainer * container = ContainerInterface::getVolumeContainer(*datapad);
if (container == NULL)
return;
for (ContainerConstIterator iter = container->begin(); iter != container->end(); ++iter)
{
const ManufactureSchematicObject * schematic = dynamic_cast<const
ManufactureSchematicObject *>((*iter).getObject());
if (schematic != NULL)
schematics.push_back(schematic);
}
} // CreatureObject::getManufactureSchematics(unfiltered)
//-----------------------------------------------------------------------
/**
* Returns a list of manufacture schematics in this creature's datapad, filtered
* by a bitmask of crafting types.
*
* @param schematics list to be filled with the schematics
* @param craftingTypes bitmask of valid crafting types
*/
void CreatureObject::getManufactureSchematics(std::vector<const ManufactureSchematicObject *> & schematics, uint32 craftingTypes)
{
const ServerObject * datapad = getDatapad();
if (datapad == NULL)
return;
const VolumeContainer * container = ContainerInterface::getVolumeContainer(*datapad);
if (container == NULL)
return;
for (ContainerConstIterator iter = container->begin(); iter != container->end(); ++iter)
{
const ManufactureSchematicObject * schematic = dynamic_cast<const
ManufactureSchematicObject *>((*iter).getObject());
if (schematic != NULL && ((schematic->getCategory() & craftingTypes) != 0))
schematics.push_back(schematic);
}
} // CreatureObject::getManufactureSchematics(filtered)
//-----------------------------------------------------------------------
bool CreatureObject::isItemEquipped(const Object & item) const
{
Object const * const itemContainedBy = ContainerInterface::getContainedByObject (item);
if (itemContainedBy)
{
return (getNetworkId () == itemContainedBy->getNetworkId ());
}
return false;
}
//-----------------------------------------------------------------------
void CreatureObject::getBehaviorTargets(int behavior, std::vector<NetworkId> &targets)
{
if (behavior < 0 || behavior >= Behaviors::NumberOfBehaviors)
{
return;
}
targets.clear();
targets.reserve(m_targetsByBehavior[behavior].size());
targets.resize(m_targetsByBehavior[behavior].size());
std::set<NetworkId>::const_iterator i;
std::vector<NetworkId>::iterator src = targets.begin();
for (i=m_targetsByBehavior[behavior].begin(); i!= m_targetsByBehavior[behavior].end(); ++i)
{
*src++=*i;
}
}
//-----------------------------------------------------------------------
int CreatureObject::getBehavior() const
{
return m_lastBehavior.get();
}
//-----------------------------------------------------------------------
int CreatureObject::getBehaviorToward(const NetworkId &target) const
{
CreatureAttitude const * const cs = getAttitudeToward(target);
if (!cs)
return 0;
return cs->m_behavior;
}
//----------------------------------------------------------------------
int CreatureObject::getNiche() const
{
const SharedCreatureObjectTemplate * sharedTemplate = safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate());
return static_cast<unsigned int>(sharedTemplate->getNiche());
}
//----------------------------------------------------------------------
SharedCreatureObjectTemplate::Species CreatureObject::getSpecies() const
{
const SharedCreatureObjectTemplate * sharedTemplate = safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate());
return static_cast<SharedCreatureObjectTemplate::Species>(sharedTemplate->getSpecies());
}
//----------------------------------------------------------------------
SharedCreatureObjectTemplate::Race CreatureObject::getRace() const
{
const SharedCreatureObjectTemplate * sharedTemplate = safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate());
return static_cast<SharedCreatureObjectTemplate::Race>(sharedTemplate->getRace());
}
//----------------------------------------------------------------------
/*
void CreatureObject::setAnimationState(CreatureObject::AnimationState state)
{
m_animState = static_cast<unsigned char>(state);
}
*/
//----------------------------------------------------------------------
void CreatureObject::setMood(uint32 mood)
{
if(isAuthoritative())
{
DEBUG_WARNING (mood > 255, ("Mood %d out of range", mood));
m_mood = static_cast<unsigned char>(mood);
}
else
{
sendControllerMessageToAuthServer(CM_setMood, new MessageQueueGenericValueType<unsigned long>(mood));
}
}
//----------------------------------------------------------------------
/**
* Sets the incapacitated state on the creature.
*
* @param flag true to incapacitate, false to recapacitate
* @param attacker who incapacitated us
*/
void CreatureObject::setIncapacitated(bool flag, const NetworkId & attackerId)
{
if (isAuthoritative())
{
if (flag)
{
clearHateList();
}
int oldPosture = getPosture();
if (flag && oldPosture != Postures::Incapacitated)
{
ScriptParams params;
params.addParam(attackerId);
if (getScriptObject()->trigAllScripts(
Scripting::TRIG_ABOUT_TO_BE_INCAPACITATED, params) == SCRIPT_OVERRIDE)
{
LOG("CustomerService", ("Combat: %s not incapacitated due to script",
PlayerObject::getAccountDescription(this).c_str()));
return;
}
LOG("CustomerService", ("Combat: %s incapacitated",
PlayerObject::getAccountDescription(this).c_str()));
// if we are crafting, end the crafting session
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL && player->isCrafting())
player->stopCrafting(false);
if (isInNpcConversation())
endNpcConversation();
setPosture(Postures::Incapacitated);
setState(States::FeignDeath, false);
// invoke incapacitation script on us
if (oldPosture != Postures::Dead)
{
params.clear();
params.addParam(attackerId);
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_INCAPACITATED, params));
if (attackerId != NetworkId::cms_invalid)
{
// invoke incapacitation script on who incapacitated us
ServerObject * attacker = safe_cast<ServerObject *>(
NetworkIdManager::getObjectById(attackerId));
if (attacker != NULL)
{
params.clear();
params.addParam(getNetworkId());
IGNORE_RETURN(attacker->getScriptObject()->trigAllScripts(
Scripting::TRIG_INCAPACITATE_TARGET, params));
TangibleObject * const tangibleAttacker = attacker->asTangibleObject();
if (tangibleAttacker != NULL)
{
tangibleAttacker->verifyHateList();
}
}
}
}
}
else if (!flag && oldPosture == Postures::Incapacitated)
{
// allow scripts to prevent recapacitation
ScriptParams params;
if (getScriptObject()->trigAllScripts(Scripting::TRIG_RECAPACITATED,
params) == SCRIPT_OVERRIDE)
{
LOG("CustomerService", ("Combat: %s not recapacitated due to script",
PlayerObject::getAccountDescription(this).c_str()));
return;
}
LOG("CustomerService", ("Combat: %s recapacitated",
PlayerObject::getAccountDescription(this).c_str()));
// recapacitate us
// Force the stand command to execute
setPosture(Postures::Upright);
}
// if we are a player, send us our new posture
if ((getController() != NULL) && !isInCombat())
{
getController()->appendMessage(
CM_setPosture,
0.0f,
new MessageQueuePosture(getPosture(), true),
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_ALL_CLIENT
);
}
}
else
{
sendControllerMessageToAuthServer(CM_setIncapacitated, new MessageQueueGenericValueType<std::pair<bool, NetworkId> >(std::make_pair(flag, attackerId)));
}
}
//----------------------------------------------------------------------
void CreatureObject::updateMovementInfo()
{
// TODO RAD - figure out a real fix for this
if (!canMove() && !isPlayerControlled())
m_stopWalkRun = 0;
//-- Retrieve walk and run multipliers from movement table. These are based on the creature's posture and states.
float walkSpeedModifier = 0.0f;
float runSpeedModifier = 0.0f;
float accelScale = 0.0f;
float turnScale = 0.0f;
getMovementTable()->getAllMovementRateScales(m_posture.get(), m_states.get(), walkSpeedModifier, runSpeedModifier, accelScale, turnScale);
m_accelScale = accelScale;
m_turnScale = turnScale;
DEBUG_WARNING((walkSpeedModifier > 0.0f) && (runSpeedModifier > 0.0f) && (walkSpeedModifier != runSpeedModifier), ("updateMovementInfo(): both walk and run speed are nonzero but not equal,walk=[%g],run=[%g].", walkSpeedModifier, runSpeedModifier));
m_movementScale = std::max(walkSpeedModifier, runSpeedModifier);
//-- Calculate walk and run speed.
m_walkSpeed = (walkSpeedModifier > 0.0f) ? getBaseWalkSpeed() : 0.0f;
m_runSpeed = (runSpeedModifier > 0.0f) ? getBaseRunSpeed() : 0.0f;
// If this creature is mounted, we want this creature's walkSpeed, runSpeed, movementScale and movementPercent set to the mount's
if (getState(States::RidingMount))
{
CreatureObject *const mountedCreature = getMountedCreature();
if (mountedCreature)
{
if (GameObjectTypes::isTypeOf (mountedCreature->getGameObjectType (), SharedObjectTemplate::GOT_vehicle) &&
mountedCreature->m_vehiclePhysicsData)
{
m_runSpeed = mountedCreature->m_vehiclePhysicsData->m_runSpeed;
m_walkSpeed = mountedCreature->m_vehiclePhysicsData->m_walkSpeed;
}
else
{
m_walkSpeed = mountedCreature->getBaseWalkSpeed();
m_runSpeed = mountedCreature->getBaseRunSpeed();
}
}
}
//-- Update fast/slow/stationary locomotion mappings.
getAvailableLocomotions();
//-- Choose locomotion.
m_locomotion = calcLocomotion();
// if the posture isn't sneak, make sure we don't have cover
if (getPosture() != Postures::Sneaking && getCover() != 0)
setCover(0);
// if we are prone and have a movement mod > 50, increase our movement rate
if (getPosture() == Postures::Prone)
{
int movementMod = getEnhancedModValue(SLOPE_MOD);
GroupObject const *group = getGroup();
if (group)
{
// get my group leader skill mod
NetworkId const &leaderId = group->getGroupLeaderId();
if (leaderId != NetworkId::cms_invalid)
{
Object const *o = NetworkIdManager::getObjectById(leaderId);
if (o)
{
ServerObject const *so = o->asServerObject();
if (so)
{
CreatureObject const *co = so->asCreatureObject();
if (co)
movementMod += co->getEnhancedModValue(GROUP_SLOPE_MOD);
}
}
}
}
if (movementMod > 50)
{
float moveScale, accelScale, turnScale;
getMovementTable()->getRates(Postures::Upright, moveScale, accelScale, turnScale);
movementMod -= 50;
if (movementMod > 50)
movementMod = 50;
float modScale = (movementMod / 50.0f) * ((moveScale / m_movementScale.get()) - 1.0f) + 1.0f;
m_movementScale = m_movementScale.get() * modScale;
}
}
//-- Mounts: update to a mount's movement info needs to propagate to
// its rider.
if (getState(States::MountedCreature))
{
typedef std::vector<CreatureObject *> Riders;
Riders riders;
getMountingRiders(riders);
Riders::const_iterator ii = riders.begin();
Riders::const_iterator iiEnd = riders.end();
for (; ii != iiEnd; ++ii)
{
CreatureObject *const rider = * ii;
if (rider)
rider->requestMovementInfoUpdate();
else
{
LOG("mounts-bug", ("CreatureObject::updateMovementInfo(): server id=[%d],mount id=[%s],has MountedCreature state but getMountingRider() returns null.", static_cast<int>(GameServer::getInstance().getProcessId()), getNetworkId().getValueString().c_str()));
detachAllRiders();
}
}
}
//-- Prevent player from moving while stunned
if (getState(States::Stunned))
{
m_turnScale = 0.0f;
m_movementScale = 0.0f;
}
}
//----------------------------------------------------------------------
/**
* Sets a creature's posture, if it's allowed for its current state. This function
* should be called directly if source is CS_player, otherwise it should be called
* from setCombatState().
*
* Note: callers should set isClientImmediate to false (the default) if changing
* posture during combat. Combat generally controls when the client visuals for
* posture will actually change. Setting isClientImmediate to true causes the client
* to drop any combat actions currently playing involving this creature and will
* make the client's visual posture change immediately.
*
* @param posture the new posture.
* @param isClientImmediate if set, the client changes visual posture immediately;
* otherwise, combat is assumed to migrate to this posture
* at the appropriate time (as instructed by the combat action
* message) or the client will revert to this new server posture
* in 5 seconds (as of this writing).
*/
void CreatureObject::setPosture(Postures::Enumerator newPosture, bool isClientImmediate)
{
//-- vehicles can't be anything but upright
if (newPosture != Postures::Upright && GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
WARNING (true, ("CreatureObject attempt to set a vehicle to posture [%d].", newPosture));
return;
}
//-- Only do server message processing if the posture changed, but always send the
// posture to the clients if isClientImmediate is true --- the client visual posture
// is unknown on the server and may be different than the server posture.
Postures::Enumerator oldPosture = m_posture.get();
bool const doServerProcessing = (oldPosture != newPosture);
if (doServerProcessing)
{
//-- Process the posture change on the server only if the posture changed.
if (!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setPosture, new MessageQueuePosture(newPosture, isClientImmediate));
return;
}
if (getMovementTable()->getLocomotion(newPosture, m_states.get(), MovementTable::kStationary) != Locomotions::Invalid)
{
Postures::Enumerator oldPosture = m_posture.get();
m_posture = newPosture;
// If we were previously sitting on a chair, clear the flag.
if(oldPosture == Postures::Sitting)
setState(States::SittingOnChair, false);
// @todo revisit this. updateMoevementInfo() indirectly changes auto delta variables. Since this isn't
// guaranteed to be the authoritative object, this is an invalid thing to do.
requestMovementInfoUpdate();
if (getScriptObject() != NULL)
{
ScriptParams params;
params.addParam(oldPosture);
params.addParam(newPosture);
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_CHANGED_POSTURE, params));
}
recomputeSlopeModPercent();
}
}
//-- Handle posture changes that should take effect immediately on the client.
if (isClientImmediate)
{
//-- Send if we're the authoritative server OR if we're not passing the message on to the authoritative server.
// This guarantees that we only send this message to the clients once.
bool const sendToClient = isAuthoritative() || !doServerProcessing;
if (sendToClient)
{
Controller *const controller = getController();
if (controller)
controller->appendMessage(CM_setPosture, 0.0f, new MessageQueuePosture(newPosture, true), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
else
DEBUG_WARNING(true, ("setPosture(): called with isClientImmediate but object id=[%s],template=[%s] has no controller.", getNetworkId().getValueString().c_str(), getObjectTemplateName()));
}
}
}
// ----------------------------------------------------------------------
/**
* Call this function to request that movement info be updated at the
* end of the next frame's alter.
*
* This function was needed to delay the updateMovementInfo() until
* after all transitive mount-related state was complete. It has the
* side effect of preventing the call from being made multiple times
* in the same frame.
*/
void CreatureObject::requestMovementInfoUpdate()
{
setLocalFlag(LocalObjectFlags::CreatureObject_RequiresMovementInfoUpdate, true);
}
//-----------------------------------------------------------------------
void CreatureObject::setCover(int cover)
{
if(!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setCover, new MessageQueueGenericValueType<int>(cover));
}
else
{
m_cover = cover;
}
}
// ----------------------------------------------------------------------
/**
* Sets the value of a state on a creature, if it's allowed for its current
* state.
*
* @param whichState which state to change
* @param value new value of the state
*/
void CreatureObject::setState(States::Enumerator whichState, bool value)
{
if (isAuthoritative())
{
if(whichState == States::Combat)
{
setInCombat(value);
return;
}
//-- vehicles can't have most states
if (value && GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
switch (whichState)
{
case States::MountedCreature:
case States::Swimming:
case States::Combat:
// these states are okay for a vehicle to have, continue on with state change processing.
break;
default:
// all other states are not okay for a vehicle to have, warn about them.
WARNING (true, ("CreatureObject id=[%s] attempt to set state id=[%d], name=[%s] on a vehicle, unsupported.", getNetworkId().getValueString().c_str(), whichState, States::getStateName(whichState)));
return;
}
}
uint64 stateFlags = m_states.get();
// enforce a no state change if the creature is a glowing Jedi
if (value)
stateFlags |= States::getStateMask(whichState);
else
stateFlags &= ~States::getStateMask(whichState);
if (m_states.get() != stateFlags)
{
m_states = stateFlags;
requestMovementInfoUpdate();
}
}
else
{
sendControllerMessageToAuthServer(CM_setState, new MessageQueueSetState(static_cast<int>(whichState), value));
}
}
//----------------------------------------------------------------------
void CreatureObject::setSayMode(uint32 sayMode)
{
if(isAuthoritative())
{
DEBUG_WARNING (sayMode > 255, ("Saymode %d out of range", sayMode));
m_sayMode = static_cast<unsigned char>(sayMode);
}
else
{
sendControllerMessageToAuthServer(CM_setSayMode, new MessageQueueGenericValueType<unsigned long>(sayMode));
}
}
//----------------------------------------------------------------------
void CreatureObject::setAnimationMood(const std::string & mood)
{
if(isAuthoritative())
{
m_animationMood = mood;
}
else
{
sendControllerMessageToAuthServer(CM_setAnimationMood, new MessageQueueGenericValueType<std::string>(mood));
}
}
//----------------------------------------------------------------------
void CreatureObject::setAttribute(Attributes::Enumerator attribute, Attributes::Value value)
{
if(!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setAttribute, new MessageQueueGenericValueType<std::pair<Attributes::Enumerator, Attributes::Value> >(std::make_pair(attribute, value)));
}
else
{
if (attribute < 0 || attribute >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attribute));
return;
}
if (!Attributes::isAttribPool(attribute))
{
WARNING(true, ("CreatureObject::setAttribute called with non-pool attribute %d on creature %s. If this is a regeneration value, call setRegenRate() instead.",
attribute, getNetworkId().getValueString().c_str()));
return;
}
const Attributes::Value currentValue = m_attributes[attribute];
IGNORE_RETURN(alterAttribute(attribute, value - currentValue, true));
}
}
//----------------------------------------------------------------------
/** Set the maximum value for an attribute. Optionally revalidate the stats for players.
*/
void CreatureObject::setMaxAttribute(Attributes::Enumerator attribute, Attributes::Value value, bool verifyPlayerMaxStats)
{
if(! isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setMaxAttribute, new MessageQueueGenericValueType<std::pair<Attributes::Enumerator, Attributes::Value> >(std::make_pair(attribute, value)));
}
else
{
if (attribute < 0 || attribute >= Attributes::NumberOfAttributes)
{
DEBUG_REPORT_LOG(true, ("attribute out of range (%d)\n", attribute));
return;
}
if (!Attributes::isAttribPool(attribute))
{
WARNING(true, ("CreatureObject::setMaxAttribute called with non-pool attribute %d on creature %s. If this is a regeneration value, call setRegenRate() instead.",
attribute, getNetworkId().getValueString().c_str()));
return;
}
m_maxAttributes.set(attribute, value);
}
}
//----------------------------------------------------------------------
void CreatureObject::setMentalState(MentalStates::Enumerator state, MentalStates::Value value)
{
if(! isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setMentalState, new MessageQueueGenericValueType<std::pair<MentalStates::Enumerator, MentalStates::Value> >(std::make_pair(state, value)));
}
else
{
if (state < 0 || state >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", state));
return;
}
if (value > m_maxMentalStates[state])
value = m_maxMentalStates[state];
else if (value < 0)
value = 0;
CreatureAttitude newState = m_currentAttitude.get();
newState.m_currentValues[state] = value;
m_currentAttitude.set(newState);
}
}
//----------------------------------------------------------------------
void CreatureObject::setMaxMentalState(MentalStates::Enumerator state, MentalStates::Value value)
{
if(isAuthoritative())
{
if (state < 0 || state >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", state));
return;
}
m_maxMentalStates.set(state, value);
}
else
{
sendControllerMessageToAuthServer(CM_setMaxMentalState, new MessageQueueGenericValueType<std::pair<MentalStates::Enumerator, MentalStates::Value> >(std::make_pair(state, value)));
}
}
//----------------------------------------------------------------------
void CreatureObject::setMentalStateDecay(MentalStates::Enumerator state, float value)
{
if(! isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setMentalStateDecay, new MessageQueueGenericValueType<std::pair<MentalStates::Enumerator, float> >(std::make_pair(state, value)));
}
else
{
if (state < 0 || state >= MentalStates::NumberOfMentalStates)
{
DEBUG_REPORT_LOG(true, ("mental state out of range (%d)\n", state));
return;
}
m_mentalStateDecays.set(state, value);
}
}
//----------------------------------------------------------------------
ServerObject *CreatureObject::getObjectInSlot(ConstCharCrcLowerString const &slotName)
{
SlottedContainer * const s = ContainerInterface::getSlottedContainer(*this);
if (s)
{
SlotId const dp = SlotIdManager::findSlotId(slotName);
Container::ContainerErrorCode tmp = Container::CEC_Success;
return safe_cast<ServerObject *>(s->getObjectInSlot(dp, tmp).getObject());
}
return 0;
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getObjectInSlot (const ConstCharCrcLowerString & slotName) const
{
return const_cast<CreatureObject *>(this)->getObjectInSlot (slotName);
}
//-----------------------------------------------------------------------
ServerObject * CreatureObject::getDatapad()
{
return getObjectInSlot (SlotNames::datapad);
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getDatapad() const
{
return const_cast<CreatureObject *>(this)->getDatapad ();
}
//----------------------------------------------------------------------
ServerObject * CreatureObject::getBank ()
{
return getObjectInSlot (SlotNames::bank);
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getBank () const
{
return const_cast<CreatureObject *>(this)->getBank ();
}
//-----------------------------------------------------------------------
ServerObject * CreatureObject::getInventory()
{
return getObjectInSlot (SlotNames::inventory);
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getInventory() const
{
return const_cast<CreatureObject *>(this)->getInventory ();
}
//----------------------------------------------------------------------
ServerObject * CreatureObject::getHair()
{
return getObjectInSlot (SlotNames::hair);
}
//------------------------------------------------------------------------
const ServerObject * CreatureObject::getHair() const
{
return const_cast<CreatureObject *>(this)->getHair ();
}
//----------------------------------------------------------------------
ServerObject * CreatureObject::getAppearanceInventory()
{
return getObjectInSlot (SlotNames::appearance);
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getAppearanceInventory() const
{
return const_cast<CreatureObject *>(this)->getAppearanceInventory ();
}
//----------------------------------------------------------------------
ServerObject * CreatureObject::getHangar()
{
return getObjectInSlot (SlotNames::hangar);
}
//----------------------------------------------------------------------
const ServerObject * CreatureObject::getHangar() const
{
return const_cast<CreatureObject *>(this)->getHangar();
}
//-----------------------------------------------------------------------
bool CreatureObject::onContainerAboutToTransfer(ServerObject * destination, ServerObject* transferer)
{
bool const baseResult = TangibleObject::onContainerAboutToTransfer(destination, transferer);
if (!baseResult)
return false;
const PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
// let's prevent CS from putting any more players in their inventories
if(player && destination)
{
const CreatureObject * creatureDestination = destination->asCreatureObject();
if(creatureDestination)
{
const PlayerObject * const playerDestination = PlayerCreatureController::getPlayerObject(creatureDestination);
if(playerDestination)
{
return false;
}
}
}
return onContainerAboutToTransferForMounts(destination, transferer);
}
/**
* Callback for when the creature is about to equip an item. Checks to make sure
* we don't equip two shields, and prevents equiping armor that would incapacitate
* us.
*
* @param item the item being equipped
* @param transferer who is causing the item to be equipped
*
* @return true to allow the transfer, false to prevent it
*/
int CreatureObject::onContainerAboutToGainItem(ServerObject& item, ServerObject* transferer)
{
TangibleObject const * const object = item.asTangibleObject();
if (object != NULL)
{
// See if this item is equippable
const char *sharedTemplateName = item.getSharedTemplateName();
if (!isAppearanceEquippable(sharedTemplateName))
{
if (getClient() != NULL)
{
StringId message("shared", "item_not_equippable");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
return Container::CEC_SilentError;
}
else
return Container::CEC_BlockedByDestinationContainer;
}
}
return TangibleObject::onContainerAboutToGainItem(item, transferer);
} // CreatureObject::onContainerAboutToGainItem
//----------------------------------------------------------------------
/**
* Callback for when the creature unequips an item.
*
* @param destination where the item was transferred to
* @param item the item that was unequipped
* @param transferer who caused the transfer
*/
void CreatureObject::onContainerLostItem(ServerObject *destination, ServerObject &item, ServerObject *transferer)
{
TangibleObject const * const tangibleObject = item.asTangibleObject();
if (tangibleObject == NULL)
return;
// check if the object applies skill mod bonuses when equipped
{
std::vector<std::pair<std::string, int> > skillModBonuses;
tangibleObject->getSkillModBonuses(skillModBonuses);
int const bonusCount = skillModBonuses.size();
if (bonusCount > 0)
{
std::vector<const char *> modNames(bonusCount);
std::vector<int> modValues(bonusCount);
for (int i = 0; i < bonusCount; ++i)
{
std::pair<std::string, int> const &bonus = skillModBonuses[i];
addModBonus(bonus.first, -bonus.second);
modNames[i] = bonus.first.c_str();
modValues[i] = -bonus.second;
}
ScriptParams params;
params.addParam(modNames);
params.addParam(modValues);
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_SKILLMODS_CHANGED, params));
}
}
// check if the object applies attribute bonuses when equipped
{
std::vector<std::pair<int, int> > attribBonuses;
tangibleObject->getAttribBonuses(attribBonuses);
int const bonusCount = attribBonuses.size();
for (int i = 0; i < bonusCount; ++i)
{
std::pair<int, int> const &bonus = attribBonuses[i];
addAttribBonus(bonus.first, -bonus.second);
}
}
// if the item is a weapon, make our current weapon our default weapon
WeaponObject const * const weaponObject = tangibleObject->asWeaponObject();
if (weaponObject != NULL && getDefaultWeapon() != NULL)
setCurrentWeapon(*getDefaultWeapon());
// check if the object is our shield
if (tangibleObject == m_shield)
m_shield = NULL;
//Update wearbles data
SlottedContainmentProperty* scp = ContainerInterface::getSlottedContainmentProperty(item);
if (scp)
{
WearableEntry e("", scp->getCurrentArrangement(), item.getNetworkId(), 0);
int index = m_wearableData.find(e);
if (index >= 0)
{
m_wearableData.erase(index);
}
}
TangibleObject::onContainerLostItem(destination, item, transferer);
} // CreatureObject::onContainerLostItem
//----------------------------------------------------------------------
/**
* Callback for when the creature equips an item.
* shields, and weapons.
*
* @param item the item that was equipped
* @param transferer who caused the transfer
*/
void CreatureObject::onContainerGainItem(ServerObject& item, ServerObject* source, ServerObject* transferer)
{
TangibleObject * const tangibleObject = item.asTangibleObject();
if (tangibleObject == NULL)
return;
// check if the object applies skill mod bonuses when equipped
{
std::vector<std::pair<std::string, int> > skillModBonuses;
tangibleObject->getSkillModBonuses(skillModBonuses);
int const bonusCount = skillModBonuses.size();
if (bonusCount > 0)
{
std::vector<const char *> modNames(bonusCount);
std::vector<int> modValues(bonusCount);
for (int i = 0; i < bonusCount; ++i)
{
std::pair<std::string, int> const &bonus = skillModBonuses[i];
addModBonus(bonus.first, bonus.second);
modNames[i] = bonus.first.c_str();
modValues[i] = bonus.second;
}
ScriptParams params;
params.addParam(modNames);
params.addParam(modValues);
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_SKILLMODS_CHANGED, params));
}
}
// check if the object applies attribute bonuses when equipped
{
std::vector<std::pair<int, int> > attribBonuses;
tangibleObject->getAttribBonuses(attribBonuses);
int const bonusCount = attribBonuses.size();
for (int i = 0; i < bonusCount; ++i)
{
std::pair<int, int> const &bonus = attribBonuses[i];
addAttribBonus(bonus.first, bonus.second);
}
}
// if the item is a weapon, make it our current weapon
WeaponObject * const weaponObject = tangibleObject->asWeaponObject();
if (weaponObject != NULL)
setCurrentWeapon(*weaponObject);
//Update wearables data
SlottedContainmentProperty* scp = ContainerInterface::getSlottedContainmentProperty(item);
if (scp)
{
Object const * inventory = 0;
Object const * datapad = 0;
Object const * appearance = 0;
SlottedContainer const * const myContainer = ContainerInterface::getSlottedContainer(*this);
if (myContainer)
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
static SlotId const inventorySlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::inventory);
if (inventorySlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(inventorySlot, tmp);
inventory = itemId.getObject();
}
SlotId datapadSlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::datapad);
if (datapadSlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(datapadSlot, tmp);
datapad = itemId.getObject();
}
SlotId appearanceInvSlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::appearance);
if (appearanceInvSlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(appearanceInvSlot, tmp);
appearance = itemId.getObject();
}
}
if ( scp->isInAppearanceSlot() || tangibleObject == inventory || tangibleObject == datapad)
{
ConstCharCrcString clientSharedTemplateNameCrcString = ObjectTemplateList::lookUp(tangibleObject->getClientSharedTemplateName());
if (clientSharedTemplateNameCrcString.isEmpty())
WARNING(true, ("Could not find crc for %s. Wearable will not be streamed to client", tangibleObject->getClientSharedTemplateName()));
else if (tangibleObject->asWeaponObject() != NULL)
{
addPackedWearable(tangibleObject->getAppearanceData(), scp->getCurrentArrangement(), tangibleObject->getNetworkId(), clientSharedTemplateNameCrcString.getCrc(),
tangibleObject->createSharedBaselinesMessage(), tangibleObject->createSharedNpBaselinesMessage());
}
else
addPackedWearable(tangibleObject->getAppearanceData(), scp->getCurrentArrangement(), tangibleObject->getNetworkId(), clientSharedTemplateNameCrcString.getCrc());
}
}
TangibleObject::onContainerGainItem(item, source, transferer);
}
// ----------------------------------------------------------------------
void CreatureObject::onContainerTransferComplete(ServerObject *oldContainer, ServerObject *newContainer)
{
TangibleObject::onContainerTransferComplete(oldContainer, newContainer);
setStatesDueToContainment();
if (!isInWorld())
setMovementStationary();
ShipObject const * const oldContainingShipObject = ShipObject::getContainingShipObject(oldContainer);
ShipObject const * const newContainingShipObject = ShipObject::getContainingShipObject(newContainer);
if (oldContainingShipObject != newContainingShipObject)
{
GroupObject * const groupObject = getGroup();
if (groupObject != 0)
{
if (oldContainingShipObject != 0)
{
if (oldContainingShipObject->getPortalProperty() != 0)
{
if (getNetworkId() == oldContainingShipObject->getOwnerId())
{
NetworkId const & shipId = oldContainingShipObject->getNetworkId();
groupObject->removePOBShip(shipId);
}
}
}
if (newContainingShipObject != 0)
{
if (newContainingShipObject->getPortalProperty() != 0)
{
groupObject->addPOBShipAndOwner(newContainingShipObject->getNetworkId(), newContainingShipObject->getOwnerId());
}
}
NetworkId const & shipId = (newContainingShipObject != 0) ? newContainingShipObject->getNetworkId() : NetworkId::cms_invalid;
groupObject->setShipForMember(getNetworkId(), shipId);
}
}
Client * const client = getClient();
if (client && ServerWorld::isSpaceScene())
{
ServerObject const * const oldTopmost = oldContainer ? safe_cast<ServerObject *>(ContainerInterface::getTopmostContainer(*oldContainer)) : this;
ServerObject * const newTopmost = newContainer ? safe_cast<ServerObject *>(ContainerInterface::getTopmostContainer(*newContainer)) : this;
if (oldTopmost != newTopmost)
{
SpaceVisibilityManager::removeClient(*client);
SpaceVisibilityManager::addClient(*client, *newTopmost);
}
}
}
// ----------------------------------------------------------------------
// this object got added to a trigger volume
void CreatureObject::onAddedToTriggerVolume(TriggerVolume & triggerVolume)
{
WARNING(!isInWorld(), ("CreatureObject (%s) not in world but being added to TriggerVolume (%s) owned by (%s)",
getDebugInformation().c_str(),
triggerVolume.getName().c_str(),
triggerVolume.getOwner().getDebugInformation().c_str()));
IGNORE_RETURN(m_triggerVolumeEntered.insert(&triggerVolume));
}
// ----------------------------------------------------------------------
// this object got removed from a trigger volume
void CreatureObject::onRemovedFromTriggerVolume(TriggerVolume & triggerVolume)
{
// NOTE: be careful about referencing the TriggerVolume because this
// function can be called from the TriggerVolume's dtor
IGNORE_RETURN(m_triggerVolumeEntered.erase(&triggerVolume));
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/**
* Computes the total attributes and max attributes for sending to clients,
*/
void CreatureObject::computeTotalAttributes ()
{
int i;
WARNING_STRICT_FATAL(!isAuthoritative(), ("computeTotalAttributes called on nonauthoritative object %s:%s", getObjectTemplateName(), getNetworkId().getValueString().c_str()));
if (!isAuthoritative())
return;
if (m_deferComputeTotalAttributes <= 0)
{
if (m_regenerationTime >= ConfigServerGame::getRegenThreshold())
{
// send updates to the client
m_regenerationTime -= ConfigServerGame::getRegenThreshold();
for (i = 0; i < 3; ++i)
{
int poolAttrib = Attributes::POOLS[i];
const float delta = static_cast<float>(floor(m_regeneration[
poolAttrib]));
if (delta >= 1.0f)
{
m_regeneration[poolAttrib] -= delta;
IGNORE_RETURN(alterAttribute(poolAttrib, static_cast<int>(
delta), true));
}
}
}
if (ConfigServerGame::getCheckAttribLimits())
{
// check and make sure our current attribs are <= our max attribs,
// for both unmodified and modified versions
for (i = 0; i < Attributes::NumberOfAttributes; ++i)
{
if (Attributes::isAttribPool(i))
{
if (getUnmodifiedAttribute(i) > getUnmodifiedMaxAttribute(i))
{
WARNING_STRICT_FATAL(true, ("Creature %s has attribute %d with "
"an unmodified current value > its max value! We will change "
"the current value! Current = %d, max = %d, "
"cur mods = %d, max mods = %d",
getNetworkId().getValueString().c_str(),
i,
static_cast<int>(getUnmodifiedAttribute(i)),
static_cast<int>(getUnmodifiedMaxAttribute(i)),
static_cast<int>(m_cachedCurrentAttributeModValues[i]),
static_cast<int>(m_cachedMaxAttributeModValues[i])));
m_attributes.set(i, getUnmodifiedMaxAttribute(i));
}
if (getAttribute(i) > getMaxAttribute(i))
{
WARNING_STRICT_FATAL(true, ("Creature %s has attribute %d with "
"a current value > its max value! Current = %d(%d), max = "
"%d(%d), cur mods = %d, max mods = %d",
getNetworkId().getValueString().c_str(),
i,
static_cast<int>(getAttribute(i)),
static_cast<int>(getUnmodifiedAttribute(i)),
static_cast<int>(getMaxAttribute(i)),
static_cast<int>(getUnmodifiedMaxAttribute(i)),
static_cast<int>(m_cachedCurrentAttributeModValues[i]),
static_cast<int>(m_cachedMaxAttributeModValues[i])));
}
}
}
}
for (i = 0; i < Attributes::NumberOfAttributes; ++i)
{
if (Attributes::isAttribPool(i))
{
m_totalAttributes.set(i, getAttribute(i) - static_cast<int>(floor(m_regeneration[i])));
m_totalMaxAttributes.set(i, getMaxAttribute(i));
}
}
}
} // CreatureObject::computeTotalAttributes
//----------------------------------------------------------------------
void CreatureObject::resetAttribute (Attributes::Enumerator attrib, Attributes::Value value)
{
setMaxAttribute (attrib, value);
setAttribute (attrib, value);
m_shockWounds = 0;
}
//----------------------------------------------------------------------
void CreatureObject::initializeAttribute(Attributes::Enumerator attrib, Attributes::Value value)
{
m_maxAttributes.set(attrib, value);
m_attributes.set (attrib, value);
}
//----------------------------------------------------------------------
/**
* Drains a creature's special abilities, based on it's drain attributes.
*
* @param action amount to drain the creature's action SA
* @param mind amount to drain the creature's mind SA
*
* @return true if the SA were drained, false if draining them would cause one
* to be < 0
*/
bool CreatureObject::drainAttributes(Attributes::Value action, Attributes::Value mind)
{
int i;
int mods[3] = {0, action, mind};
int deltas[3];
// determine the amount to drain and make sure it won't incapaciate the creature
for (i = 0; i < 3; ++i)
{
if (mods[i] > 0)
{
deltas[i] = getAttribute(Attributes::POOLS[i]) - mods[i];
if (deltas[i] < 0)
return false;
}
else
deltas[i] = -1;
}
// drain the attributes
for (i = 0; i < 3; ++i)
{
if (deltas[i] >= 0)
alterAttribute(Attributes::POOLS[i], -mods[i], false);
}
return true;
} // CreatureObject::drainAttributes
//----------------------------------------------------------------------
/**
* Tests draining an attribute, but does not actually modify the attrib.
*
* @param attribute the attribute to test
* @param value the amount to drain the attribute by
*
* @return the amount the attribute would be drained by, or -1 on error
*/
int CreatureObject::testDrainAttribute(Attributes::Enumerator attribute,
Attributes::Value value) const
{
// we can only drain action and mind
if (attribute != Attributes::Action && attribute != Attributes::Mind)
return -1;
if (getAttribute(attribute) - value <= 0)
return -1;
return static_cast<int>(value);
} // CreatureObject::testDrainAttribute
//----------------------------------------------------------------------
void CreatureObject::onClientReady(Client *c)
{
TangibleObject::onClientReady(c);
// check cell permissions
Object * const attachedTo = getAttachedTo();
if (attachedTo)
{
ServerObject * const serverAttachedTo = attachedTo->asServerObject();
if (serverAttachedTo)
{
CellObject * const cellObject = serverAttachedTo->asCellObject();
if (cellObject && !cellObject->isAllowed(*this))
{
BuildingObject * const ownerBuilding = cellObject->getOwnerBuilding();
if (ownerBuilding)
ownerBuilding->expelObject(*this);
}
}
}
GroupObject *group = getGroup();
if (group)
group->onGroupMemberConnect(getNetworkId());
// update the client with the list of mods currently on the character
// but first, remove any mods from the list that has expired
uint32 const gameTimeSeconds = ServerClock::getInstance().getGameTimeSeconds();
std::vector<uint32> expiredMods;
std::vector<uint32>::const_iterator iterTimedMod = m_timedMod.begin();
std::vector<float>::const_iterator iterTimedModDuration = m_timedModDuration.begin();
std::vector<uint32>::const_iterator iterTimedModUpdateTime = m_timedModUpdateTime.begin();
for (; iterTimedMod != m_timedMod.end(); ++iterTimedMod, ++iterTimedModDuration, ++iterTimedModUpdateTime)
{
if (*iterTimedModDuration >= 0)
{
if ((*iterTimedModUpdateTime + static_cast<uint32>(*iterTimedModDuration)) < gameTimeSeconds)
expiredMods.push_back(*iterTimedMod);
}
}
int index;
for (iterTimedMod = expiredMods.begin(); iterTimedMod != expiredMods.end(); ++iterTimedMod)
{
index = m_timedMod.find(*iterTimedMod);
if (index >= 0)
{
m_timedMod.erase(index);
m_timedModDuration.erase(index);
m_timedModUpdateTime.erase(index);
}
}
// send the mods to the client
iterTimedMod = m_timedMod.begin();
iterTimedModDuration = m_timedModDuration.begin();
iterTimedModUpdateTime = m_timedModUpdateTime.begin();
for (; iterTimedMod != m_timedMod.end(); ++iterTimedMod, ++iterTimedModDuration, ++iterTimedModUpdateTime)
{
if (*iterTimedModDuration >= 0)
sendTimedModData(*iterTimedMod, *iterTimedModDuration - static_cast<float>(gameTimeSeconds - *iterTimedModUpdateTime), false);
else
sendTimedModData(*iterTimedMod, -1.0f, false);
}
// update the client with the list of cooldowns currently on the character
// visual only cooldown timers are stored in objvars
DynamicVariableList const & objvars = getObjVars();
std::map<int, std::pair<int, int> > visualCooldownTimers;
for (DynamicVariableList::MapType::const_iterator iterObjVar = objvars.begin(); iterObjVar != objvars.end(); ++iterObjVar)
{
if (iterObjVar->first.find("visualCooldown.beginTime.") == 0)
{
int cooldownGroupCrc = 0;
if (1 == sscanf(iterObjVar->first.c_str(), "visualCooldown.beginTime.%d", &cooldownGroupCrc))
{
int beginTime = 0;
if (iterObjVar->second.get(beginTime))
{
std::map<int, std::pair<int, int> >::iterator iterFind = visualCooldownTimers.find(cooldownGroupCrc);
if (iterFind == visualCooldownTimers.end())
{
visualCooldownTimers[cooldownGroupCrc] = std::make_pair(beginTime, 0);
}
else
{
iterFind->second.first = beginTime;
}
}
}
}
else if (iterObjVar->first.find("visualCooldown.endTime.") == 0)
{
int cooldownGroupCrc = 0;
if (1 == sscanf(iterObjVar->first.c_str(), "visualCooldown.endTime.%d", &cooldownGroupCrc))
{
int endTime = 0;
if (iterObjVar->second.get(endTime))
{
std::map<int, std::pair<int, int> >::iterator iterFind = visualCooldownTimers.find(cooldownGroupCrc);
if (iterFind == visualCooldownTimers.end())
{
visualCooldownTimers[cooldownGroupCrc] = std::make_pair(0, endTime);
}
else
{
iterFind->second.second = endTime;
}
}
}
}
}
char buffer[256];
int const currentGameTime = static_cast<int>(ServerClock::getInstance().getGameTimeSeconds());
for (std::map<int, std::pair<int, int> >::const_iterator iterCooldown = visualCooldownTimers.begin(); iterCooldown != visualCooldownTimers.end(); ++iterCooldown)
{
// send any unexpired cooldowns to the client
if ((iterCooldown->second.first > 0) && (iterCooldown->second.second > 0) && (iterCooldown->second.first <= currentGameTime) && (currentGameTime < iterCooldown->second.second))
{
CreatureController *controller = getCreatureController();
if (controller)
{
MessageQueueCommandTimer *msg = new MessageQueueCommandTimer(
0,
iterCooldown->first,
-1,
0);
msg->setCurrentTime(MessageQueueCommandTimer::F_cooldown, static_cast<float>(currentGameTime - iterCooldown->second.first));
msg->setMaxTime (MessageQueueCommandTimer::F_cooldown, static_cast<float>(iterCooldown->second.second - iterCooldown->second.first));
controller->appendMessage(
static_cast< int >( CM_commandTimer ),
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
}
else
{
// cleanup the objvars for any expired/invalid cooldowns
snprintf(buffer, sizeof(buffer)-1, "visualCooldown.beginTime.%d", iterCooldown->first);
buffer[sizeof(buffer)-1] = '\0';
removeObjVarItem(buffer);
snprintf(buffer, sizeof(buffer)-1, "visualCooldown.endTime.%d", iterCooldown->first);
buffer[sizeof(buffer)-1] = '\0';
removeObjVarItem(buffer);
}
}
// command cooldown timers are stored in the command queue
CommandQueue const * commandQueue = getCommandQueue();
if (commandQueue && getClient())
{
double const currentTime = Clock::getCurrentTime();
uint32 currentCooldownTimeSecond = 0;
uint32 maxCooldownTimeSecond = 0;
std::map<uint32, std::pair<double, double> > const & cooldowns = commandQueue->getCooldowns().getMap();
for (std::map<uint32, std::pair<double, double> >::const_iterator cooldownsIter = cooldowns.begin(); cooldownsIter != cooldowns.end(); ++cooldownsIter)
{
if ((cooldownsIter->second.first <= currentTime) && (currentTime < cooldownsIter->second.second))
{
// don't bother sending cooldown with less than 1 second left because
// by the time it gets to the client, it will probably have already expired
currentCooldownTimeSecond = static_cast<uint32>(currentTime - cooldownsIter->second.first);
maxCooldownTimeSecond = static_cast<uint32>(cooldownsIter->second.second - cooldownsIter->second.first);
if (currentCooldownTimeSecond < maxCooldownTimeSecond)
{
GenericValueTypeMessage<std::pair<uint32, std::pair<uint32, uint32> > > commandCooldown("SetCommandCooldown", std::make_pair(cooldownsIter->first, std::make_pair(currentCooldownTimeSecond, maxCooldownTimeSecond)));
getClient()->send(commandCooldown, true);
}
}
}
}
// sync chat character count with chat server
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player && (player->getChatSpamTimeEndInterval() > 0))
{
time_t timeUnsquelch = static_cast<time_t>(player->getSecondsUntilUnsquelched());
if (timeUnsquelch > 0)
timeUnsquelch += ::time(NULL);
GenericValueTypeMessage<std::pair<std::pair<std::pair<NetworkId, int>, int>, std::pair<int, int> > > chatStatistics("ChatStatisticsGS", std::make_pair(std::make_pair(std::make_pair(getNetworkId(), static_cast<int>(timeUnsquelch)), player->getChatSpamTimeEndInterval()), std::make_pair(player->getChatSpamSpatialNumCharacters(), player->getChatSpamNonSpatialNumCharacters())));
Chat::sendToChatServer(chatStatistics);
}
// if necessary, announce TCG monthly bonus booster pack and Swg Tcg Beta
if (getClient() && !getClient()->isUsingAdminLogin())
{
VeteranRewardManager::announceMonthlyBonusBoosterPack(*this);
if (ConfigServerGame::getAccountFeatureIdForTcgBetaAnnouncement() > 0)
VeteranRewardManager::announceSwgTcgBeta(*this);
}
// Make sure our player has the server's speed maximum
PlayerCreatureController const * playerController = safe_cast<PlayerCreatureController const *>(getCreatureController());
if(playerController)
{
GenericValueTypeMessage<float> const msg("fca11a62d23041008a4f0df36aa7dca6", playerController->getServerSpeedForPlayer());
Client const * const client = getClient();
if (client)
return client->send(msg, true);
}
}
// ----------------------------------------------------------------------
void CreatureObject::onClientAboutToLoad()
{
if (isAuthoritative())
setInvulnerabilityTimer(ConfigServerGame::getCreatureLoadInvulnerableTimeWithoutClient());
TangibleObject::onClientAboutToLoad();
}
// ----------------------------------------------------------------------
void CreatureObject::onLoadingScreenComplete()
{
TangibleObject::onLoadingScreenComplete();
if (isAuthoritative() && isInvulnerable())
setInvulnerabilityTimer(ConfigServerGame::getCreatureLoadInvulnerableTimeWithClient());
}
// ----------------------------------------------------------------------
bool CreatureObject::monitorCreatureMovement(const CachedNetworkId &ofTarget, float i_skittishness, float i_curve)
{
Object * object = ofTarget.getObject();
if (!object || object == this)
return false;
Archive::AutoDeltaMap<NetworkId, MonitoredCreatureMovement>::const_iterator i = m_monitoredCreatureMovements.find(ofTarget);
MonitoredCreatureMovement newMovement;
if (i == m_monitoredCreatureMovements.end())
{
newMovement.m_lastDistance = object->getPosition_w().magnitudeBetween(getPosition_w());
newMovement.m_skittishness = i_skittishness;
newMovement.m_curve = i_curve;
}
else
{
newMovement = i->second;
newMovement.m_skittishness = i_skittishness;
newMovement.m_curve = i_curve;
}
if (newMovement != i->second)
{
m_monitoredCreatureMovements.set(ofTarget, newMovement);
}
return true;
}
//----------------------------------------------------------------------
bool CreatureObject::ignoreCreatureMovement(const CachedNetworkId &ofTarget)
{
Archive::AutoDeltaMap<NetworkId, MonitoredCreatureMovement>::const_iterator i = m_monitoredCreatureMovements.find(ofTarget);
if (i == m_monitoredCreatureMovements.end())
return false;
m_monitoredCreatureMovements.erase(ofTarget);
return true;
}
//----------------------------------------------------------------------
void CreatureObject::reportMonitoredCreatures(float time)
{
Vector ownerPosition = getPosition_w();
std::vector<NetworkId> toRemove;
Archive::AutoDeltaMap<NetworkId, MonitoredCreatureMovement>::const_iterator i;
for (i=m_monitoredCreatureMovements.begin(); i!=m_monitoredCreatureMovements.end(); ++i)
{
CachedNetworkId cnid(i->first);
Object * o = cnid.getObject();
if (o)
{
MonitoredCreatureMovement newMovement = i->second;
float lastDistance = newMovement.m_lastDistance;
float currentDistance = o->getPosition_w().magnitudeBetween(ownerPosition);
float approach = lastDistance - currentDistance;
//float tangentVelocity = sqrt(((mobDelta - targetDelta)/i_time).magnitudeSquared() - approachVelocity * approachVelocity);
// calculate the fear delta
if (approach > 0)
{
float approachVelocity = approach / time;
float totalDistance = getApproachTriggerRange();
float percent = 1 - ((lastDistance + currentDistance) * 0.5f) / totalDistance;
if (percent > 1)
percent = 1;
else if (percent < 0)
percent = 0;
if (i->second.m_curve < 0)
{
percent = -percent;
}
float absCurve = abs(i->second.m_curve);
float scale = pow(percent, absCurve) * (1+absCurve);
float speedScale = float(sqrt(approachVelocity / getRunSpeed()));
float fearDelta = approach * i->second.m_skittishness * speedScale * scale;
setMentalStateToward(i->first, MentalStates::Fear, getMentalStateToward(i->first, MentalStates::Fear)+fearDelta);
}
newMovement.m_lastDistance = currentDistance;
m_monitoredCreatureMovements.set(i->first, newMovement);
}
else
{
toRemove.push_back(i->first);
}
}
std::vector<NetworkId>::iterator removeIt;
for (removeIt = toRemove.begin(); removeIt != toRemove.end(); ++removeIt)
{
m_monitoredCreatureMovements.erase(*removeIt);
}
}
//-----------------------------------------------------------------------
void CreatureObject::doWarmupChecks(Command const &command, NetworkId const &targetId, Unicode::String const &params, Command::ErrorCode &status, int &statusDetail)
{
if (doesLocomotionInvalidateCommand(command))
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to locomotion %d",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str(),
getLocomotion()));
}
status = Command::CEC_Locomotion;
statusDetail = static_cast<int>(getLocomotion());
}
else if (isPlayerControlled() && command.m_characterAbility.size() && !hasCommand(command.m_characterAbility))
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to ability %s",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str(),
command.m_characterAbility.c_str()));
}
status = Command::CEC_Ability;
}
// target verification - don't execute the command if a target is
// required and none is present, or if there is never a target and
// one is specified
else if ( (command.m_targetType == Command::CTT_None && targetId != NetworkId::cms_invalid)
|| (command.m_targetType == Command::CTT_Required && targetId == NetworkId::cms_invalid))
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to target type",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str()));
}
status = Command::CEC_TargetType;
}
else
{
// target range checking
if (command.m_targetType != Command::CTT_None && targetId != NetworkId::cms_invalid && command.m_maxRangeToTargetSquared != 0.0f)
{
Object *targetObj = NetworkIdManager::getObjectById(targetId);
if (!targetObj)
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to range to target",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str()));
}
status = Command::CEC_TargetRange;
}
else
{
TangibleObject const * const tangible = dynamic_cast<TangibleObject *>(targetObj);
if(tangible)
{
float distance = this->getDistanceBetweenCollisionSpheres_w(*tangible);
distance = sqr(distance);
if(distance > command.m_maxRangeToTargetSquared)
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to range to target",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str()));
}
status = Command::CEC_TargetRange;
}
}
else
{
if(findPosition_w().magnitudeBetweenSquared(targetObj->findPosition_w()) > command.m_maxRangeToTargetSquared)
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to range to target",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str()));
}
status = Command::CEC_TargetRange;
}
}
}
}
if (status == Command::CEC_Success)
{
// make sure that the command is allowed in all states set on the creature currently
for (States::Enumerator i = 0; i < States::NumberOfStates; ++i)
{
// @todo: don't gate npc command usage on swimming
if (!isPlayerControlled() && i == States::Swimming)
continue;
if (getState(i) && !command.m_statePermissions[i])
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to state %d",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str(),
i));
}
status = Command::CEC_StateMustNotHave;
statusDetail = i;
break;
}
if (!getState(i) && command.m_stateRequired[i])
{
if (ConfigServerGame::getLogAllCommands())
{
LOG(
"Command",
("%s>%s %s ignored due to missing required state %d",
getNetworkId().getValueString().c_str(),
targetId.getValueString().c_str(),
command.m_commandName.c_str(),
i));
}
status = Command::CEC_StateMustHave;
statusDetail = i;
break;
}
}
}
}
}
// ----------------------------------------------------------------------
bool CreatureObject::isDisabled() const
{
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
return TangibleObject::isDisabled ();
return (isIncapacitated() || isDead());
}
// -------------------------------------------------------------------
bool CreatureObject::makeDead(const NetworkId & killer, const NetworkId & corpse)
{
if (isAuthoritative())
{
if (isDead())
{
clearHateList();
return true;
}
ScriptParams params;
params.addParam(killer);
params.addParam(corpse);
if (getScriptObject()->trigAllScripts(Scripting::TRIG_DEATH, params) != SCRIPT_CONTINUE)
return false;
clearHateList();
// if we are crafting, end the crafting session
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL && player->isCrafting())
player->stopCrafting(false);
// if we are in a conversation, end it
if (isInNpcConversation())
endNpcConversation();
// remove all attrib and skill mod mods
typedef Archive::AutoDeltaMap<uint32, CreatureMod>::const_iterator ModIter;
for (ModIter i = m_attributeModList.begin(); i != m_attributeModList.end();)
{
const CreatureMod & m = (*i).second;
bool remove = true;
//check to see if this attribute mod comes from a buff that should not be removed on death
const char * rawModName = AttribModNameManager::getInstance().getAttribModName(m.mod.tag);
if(rawModName)
{
std::string tmpModName(rawModName);
//strip the attribute number from the mod name to get the raw buff name
//this is a little fragile because we are looking up the name of the
//buff, trusting that the mod name will be based on it
//in BuffManager.cpp, attributes that are assigned by a buff have the
//mod name as ("%s_%d", buffname, effectNum) so we will check to see if
//the name matches that format and if the resulting buff name is flagged
//to not be removed on death.
int namelength = tmpModName.size();
if(namelength > 2 && tmpModName[namelength-2] == '_' && tmpModName[namelength-1] >= '0' && tmpModName[namelength-1] <= '9')
{
std::string buffname = tmpModName.substr(0,namelength-2);
uint32 buffNameCrc = Crc::normalizeAndCalculate(buffname.c_str());
remove = BuffManager::getIsBuffRemovedOnDeath(buffNameCrc);
// Check to see if this is a Pvp related death and we should ignore the removal.
if(remove && BuffManager::getDoesBuffDecayOnPvPDeath(buffNameCrc)) // We only care about buffs that would normally be removed on death and have the decay flag.
{
Object * killerObj = NetworkIdManager::getObjectById(killer);
if(killerObj && killerObj->asServerObject() && killerObj->asServerObject()->asCreatureObject())
{
CreatureObject * killerCreature = killerObj->asServerObject()->asCreatureObject();
PlayerObject * const playerKiller = PlayerCreatureController::getPlayerObject(killerCreature);
if(playerKiller)
remove = false; // Buffs that decay on PvP death don't get removed on PvP death.
else if(killerCreature->getMasterId() != NetworkId::cms_invalid) // Check to see if this is some type of pet.
{
Object * masterObj = NetworkIdManager::getObjectById(killerCreature->getMasterId());
if(masterObj && masterObj->asServerObject() && masterObj->asServerObject()->asCreatureObject())
{
CreatureObject * masterCreature = masterObj->asServerObject()->asCreatureObject();
PlayerObject * const masterPlayer = PlayerCreatureController::getPlayerObject(masterCreature); // Pet has a valid player as a master. Pvp related.
if(masterPlayer)
remove = false;
}
}
}
}// End PvP check
}
}
if(remove)
{
if (AttribMod::isSkillMod(m.mod))
{
const char * skillModName = AttribModNameManager::getInstance().getAttribModName(m.mod.skill);
if (skillModName != NULL)
addModValue(skillModName, -m.maxVal, true);
}
// if the mod is visible, we need to tell the player it's being
// removed
if (m.mod.flags & AttribMod::AMF_visible)
{
sendCancelTimedMod(m.mod.tag);
}
// if the mod is flagged as having a callback, call the script
if (m.mod.flags & AttribMod::AMF_triggerOnDone)
{
const char * modName = AttribModNameManager::getInstance().getAttribModName(m.mod.tag);
if (modName != NULL)
{
ScriptParams params;
params.addParam(modName);
params.addParam(true);
if (AttribMod::isAttribMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_ATTRIB_MOD_DONE, params));
}
else if (AttribMod::isSkillMod(m.mod))
{
IGNORE_RETURN(getScriptObject()->trigAllScripts(
Scripting::TRIG_SKILL_MOD_DONE, params));
}
}
}
i = m_attributeModList.erase(i);
}
else
{
++i;
}
}
for (int j = 0; j < Attributes::NumberOfAttributes; ++j)
{
m_cachedCurrentAttributeModValues.set(j, 0);
m_cachedMaxAttributeModValues.set(j, 0);
}
computeTotalAttributes();
LOG("CustomerService", ("Attribs: removed all attrib and skillmod mods "
"from %s due to death.",
PlayerObject::getAccountDescription(this).c_str()));
if (!isPlayerControlled())
setDefaultAlterTime(AlterResult::cms_keepNoAlter);
//-- If we're in combat, the client will take care of arranging for
// a suitable transition to the dead posture; otherwise, make it
// happen immediately.
setPosture(Postures::Dead, !isInCombat());
}
else
{
sendControllerMessageToAuthServer(CM_makeDead, new MessageQueueGenericValueType<
std::pair<NetworkId, NetworkId> >(std::make_pair(killer, corpse)));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::makeNotDead()
{
if (!isDead())
return true;
if (!isPlayerControlled())
setDefaultAlterTime(AlterResult::cms_alterQuickly);
setPosture(Postures::Prone, true);
testIncapacitation(NetworkId::cms_invalid);
return true;
}
//--------------------------------------------------------------------
Locomotions::Enumerator CreatureObject::calcLocomotion() const
{
Locomotions::Enumerator ret = Locomotions::Invalid;
if (m_stopWalkRun.get() > 1)
ret = m_fastLocomotion;
if (ret == Locomotions::Invalid && m_stopWalkRun.get() > 0)
ret = m_slowLocomotion;
if (ret == Locomotions::Invalid)
ret = m_stationaryLocomotion;
return ret;
}
//--------------------------------------------------------------------
bool CreatureObject::canMove() const
{
return getMovementTable()->hasMovingLocomotion(m_posture.get(), m_states.get()) && !getObjVars().hasItem("forceNoMovement");
}
//----------------------------------------------------------------------
void CreatureObject::setScaleFactor(float scale)
{
if(isAuthoritative())
{
scale = std::max (0.0f, scale);
m_scaleFactor = scale;
setScale (Vector::xyz111 * scale);
}
else
{
sendControllerMessageToAuthServer(CM_setScaleFactor, new MessageQueueGenericValueType<float>(scale));
}
}
//----------------------------------------------------------------------
/**
* Gets the approximate height of the creature, modified by it's posture.
*
* @return the creature's height
*/
float CreatureObject::getHeight() const
{
float height = TangibleObject::getHeight();
height *= getMovementTable()->getCanSeeHeightMod(m_posture.get(), m_states.get());
return height;
} // CreatureObject::getHeight
//----------------------------------------------------------------------
/**
* Gets the approximate radius of the object.
*
* @return the object's radius
*/
float CreatureObject::getRadius() const
{
const SharedCreatureObjectTemplate * myTemplate = safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate());
return myTemplate->getCollisionRadius() * std::max(getScale().x, getScale().z);
} // CreatureObject::getHeight
//----------------------------------------------------------------------
Object const * CreatureObject::getStandingOn() const
{
CollisionProperty const * collision = getCollisionProperty();
if(collision)
{
return collision->getStandingOn();
}
else
{
return NULL;
}
}
//----------------------------------------------------------------------
float CreatureObject::getFarNetworkUpdateRadius() const
{
if (ServerWorld::getSceneId() == NewbieTutorial::getSceneId())
return 0.f;
float height = TangibleObject::getHeight();
float radius;
if (height < ConfigServerGame::getMaxSmallCreatureHeight())
radius = ConfigServerGame::getSmallCreatureUpdateRadius();
else if (height < ConfigServerGame::getMaxMediumCreatureHeight())
radius = ConfigServerGame::getMediumCreatureUpdateRadius();
else
radius = ConfigServerGame::getLargeCreatureUpdateRadius();
return radius;
}
//----------------------------------------------------------------------
void CreatureObject::setShockWounds(int wound)
{
static const StringId SHOCK_WOUND_ID("cbt_spam", "shock_wound");
if(isAuthoritative())
{
// Use TangibleObject::isInvulnerable so we don't account for the invulnerability timer
if (TangibleObject::isInvulnerable())
return;
if (wound < 0)
wound = 0;
else if (wound > 1000)
wound = 1000;
if (wound != m_shockWounds.get())
{
if (isInCombat())
{
ProsePackage pp;
pp.stringId = SHOCK_WOUND_ID;
pp.digitInteger = wound - m_shockWounds.get();
Unicode::String oob;
OutOfBandPackager::pack(pp, 0, oob);
MessageQueueCombatSpam spam(oob, false, false, false, 1);
performCombatSpam (spam, true, false, false);
}
m_shockWounds = wound;
}
}
else
{
sendControllerMessageToAuthServer(CM_setShockWounds, new MessageQueueGenericValueType<int>(wound));
}
}
//----------------------------------------------------------------------
void CreatureObject::setLookAtTarget(const NetworkId& id)
{
if(isAuthoritative())
{
m_lookAtTarget.set(id);
}
else
{
sendControllerMessageToAuthServer(CM_setLookAtTarget, new MessageQueueGenericValueType<NetworkId>(id));
}
ShipObject * const ship = getPilotedShip();
if(ship)
{
ship->setLookAtTargetFromPilot(id);
}
}
//----------------------------------------------------------------------
void CreatureObject::setIntendedTarget(const NetworkId& id)
{
if(isAuthoritative())
{
DEBUG_WARNING(true, ("CreatureObject::setIntendedTarget: '%s'", id.getValueString().c_str()));
m_intendedTarget.set(id);
}
else
{
sendControllerMessageToAuthServer(CM_setIntendedTarget, new MessageQueueGenericValueType<NetworkId>(id));
}
}
//-----------------------------------------------------------------------
void CreatureObject::setMovementStationary()
{
if (isAuthoritative())
m_stopWalkRun = 0;
}
//-----------------------------------------------------------------------
void CreatureObject::setMovementWalk()
{
if (isAuthoritative())
m_stopWalkRun = 1;
}
//-----------------------------------------------------------------------
void CreatureObject::setMovementRun()
{
if (isAuthoritative())
m_stopWalkRun = 2;
}
//--------------------------------------------------------------------
bool CreatureObject::setSlopeModAngle(float angle)
{
if(isAuthoritative())
{
m_slopeModAngle = angle;
}
else
{
sendControllerMessageToAuthServer(CM_setSlopeModAngle, new MessageQueueGenericValueType<float>(angle));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::setSlopeModPercent(float percent)
{
if(isAuthoritative())
{
if (percent < 0.0f)
percent = 0.0f;
else if (percent > 1.0f)
percent = 1.0f;
m_baseSlopeModPercent = percent;
//
// modify the percent reduced due to skill mods
//
// get my skill mod
int movementMod = getEnhancedModValue(SLOPE_MOD);
if (getGroup() != NULL)
{
// get my group leader skill mod
const NetworkId & leaderId = getGroup()->getGroupLeaderId();
if (leaderId != NetworkId::cms_invalid)
{
const CreatureObject * leader = safe_cast<const CreatureObject *>(
NetworkIdManager::getObjectById(leaderId));
if (leader != NULL)
{
movementMod += leader->getEnhancedModValue(GROUP_SLOPE_MOD);
}
}
}
if (movementMod > 100)
movementMod = 100;
switch (getPosture())
{
case Postures::Upright:
if (movementMod > 50)
movementMod = 50;
if (movementMod > 0 && percent < 1.0f)
{
percent += (1.0f - percent) * ((float)movementMod / 50.0f);
}
break;
case Postures::Prone:
case Postures::Sneaking:
// movementMod -= 50;
// if (movementMod > 0 && percent < 1.0f)
// {
// percent += (1.0f - percent) * ((float)movementMod / 50.0f);
// }
// break;
case Postures::Crouched:
case Postures::Blocking:
case Postures::Climbing:
case Postures::Flying:
case Postures::LyingDown:
case Postures::Sitting:
case Postures::SkillAnimating:
case Postures::DrivingVehicle:
case Postures::RidingCreature:
case Postures::KnockedDown:
case Postures::Incapacitated:
case Postures::Dead:
default:
break;
}
m_slopeModPercent = percent;
}
else
{
sendControllerMessageToAuthServer(CM_setSlopeModPercent, new MessageQueueGenericValueType<float>(percent));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::setWaterModPercent(float percent)
{
if(isAuthoritative())
{
m_waterModPercent = percent;
}
else
{
sendControllerMessageToAuthServer(CM_setWaterModPercent, new MessageQueueGenericValueType<float>(percent));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::setMovementScale(float scale)
{
if(isAuthoritative())
{
m_movementScale = scale;
}
else
{
sendControllerMessageToAuthServer(CM_setMovementScale, new MessageQueueGenericValueType<float>(scale));
}
return true;
}
// ----------------------------------------------------------------------
bool CreatureObject::setMovementPercent(float percent)
{
if(isAuthoritative())
{
m_movementPercent = percent;
}
else
{
sendControllerMessageToAuthServer(CM_setMovementPercent, new MessageQueueGenericValueType<float>(percent));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::setTurnPercent(float percent)
{
if(isAuthoritative())
{
m_turnScale = percent;
}
else
{
sendControllerMessageToAuthServer(CM_setTurnPercent, new MessageQueueGenericValueType<float>(percent));
}
return true;
}
//--------------------------------------------------------------------
bool CreatureObject::setAccelScale(float scale)
{
if(isAuthoritative())
{
m_accelScale = scale;
}
else
{
sendControllerMessageToAuthServer(CM_setAccelScale, new MessageQueueGenericValueType<float>(scale));
}
return true;
}
// ----------------------------------------------------------------------
bool CreatureObject::setAccelPercent(float percent)
{
if(isAuthoritative())
{
m_accelPercent = percent;
}
else
{
sendControllerMessageToAuthServer(CM_setAccelPercent, new MessageQueueGenericValueType<float>(percent));
}
return true;
}
//--------------------------------------------------------------------
/**
* Instruct the clients to display the visuals for having this creature
* sit on the specified chair-like target object in the specified position.
*
* For chairs, where there is only a single spot to sit in, the positionIndex
* should be zero. For couches where, theoretically, we could have multiple
* creatures sit, the value should represent the space in which the player
* is sitting.
*
* @param targetObjectId the network id for the object on which the creature
* will sit.
* @param positionIndex the 0-based index of the position of the chair-like object
* in which the creature will sit. Typically this will be zero.
*/
void CreatureObject::sitOnObject(NetworkId const &chairCellId, Vector const &chairPosition_p)
{
if (isAuthoritative())
{
// you can't sit on something in a different cell than you
if (ContainerInterface::getContainedByProperty(*this)->getContainedByNetworkId() != chairCellId)
return;
//-- If the chair is more than the allowed distance away from the authoritative position for the creature, log the attempt and ignore.
Vector const creaturePosition_p = getPosition_p();
float const separationSquared = creaturePosition_p.magnitudeBetweenSquared(chairPosition_p);
if (separationSquared > MessageQueueSitOnObject::cs_maximumChairRangeSquared)
{
LOG("move_validation", ("CreatureObject::sitOnObject: object id=[%s] out of range for specified chair position, separation=[%.2f] meters.", getNetworkId().getValueString().c_str(), separationSquared));
return;
}
//-- Get the controller.
Controller * const controller = getController();
NetworkController * const networkController = safe_cast<NetworkController *>(controller);
ServerController * const serverController = safe_cast<ServerController *>(networkController);
NOT_NULL(serverController);
// teleport the player to the chairs location
// orientation doesn't matter in this case
Transform transform;
transform.setPosition_p(chairPosition_p);
serverController->teleport(transform, ContainerInterface::getContainingCellObject(*this));
//-- Send the SitOnObject controller message to all clients.
serverController->appendMessage(CM_sitOnObject, 0.0f, new MessageQueueSitOnObject(chairCellId, chairPosition_p), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT);
//-- Set the server posture to sitting & set SittingInChair state.
setPosture(Postures::Sitting);
setState(States::SittingOnChair, true);
}
else
{
sendControllerMessageToAuthServer(CM_sitOnObject, new MessageQueueSitOnObject(chairCellId, chairPosition_p));
}
}
// ----------------------------------------------------------------------
void CreatureObject::onBiographyRetrieved(const NetworkId &owner, const Unicode::String &bio)
{
//TODO: Biography handling code
//DEBUG_REPORT_LOG(true,("Biography for object %s received: %s\n",owner.getValueString().c_str(), Unicode::wideToNarrow(bio).c_str()));
CreatureController *creatureController = getCreatureController();
if (creatureController != NULL)
{
typedef std::pair<NetworkId, Unicode::String> Payload;
MessageQueueGenericValueType<Payload> * const msg = new MessageQueueGenericValueType<Payload>(Payload(owner, bio));
creatureController->appendMessage(static_cast<int>(CM_biographyRetrieved), 0.0f, msg, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
}
//-----------------------------------------------------------------------
void CreatureObject::onCharacterMatchRetrieved(MatchMakingCharacterResult const &results)
{
CreatureController *creatureController = getCreatureController();
if (creatureController != NULL)
{
typedef std::pair<NetworkId, Unicode::String> Payload;
MessageQueueGenericValueType<MatchMakingCharacterResult> * const msg = new MessageQueueGenericValueType<MatchMakingCharacterResult>(results);
creatureController->appendMessage(static_cast<int>(CM_characterMatchRetrieved), 0.0f, msg, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
}
//-----------------------------------------------------------------------
void CreatureObject::setLocomotion(Locomotions::Enumerator posture)
{
if (!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_setLocomotion, new MessageQueueGenericValueType<Locomotions::Enumerator>(posture));
}
else
{
Postures::Enumerator newPosture;
MovementTable::eLocomotionSpeed speed = getMovementTable()->getLocomotionData(posture, newPosture);
if (speed != MovementTable::kInvalid)
{
setPosture(newPosture);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::speakText(MessageQueueSpatialChat const &spatialChat)
{
// a speech type may only be used if it either has no skills listed as providing it, or the creature has
// at least one of the skills listed.
std::vector<std::string> const &skillsProviding = SpatialChatManager::getSkillsProviding(spatialChat.getChatType());
if (!skillsProviding.empty())
{
bool found = false;
for (std::vector<std::string>::const_iterator i = skillsProviding.begin(); i != skillsProviding.end(); ++i)
{
if (hasCommand(*i))
{
found = true;
break;
}
}
if (!found)
return;
}
TangibleObject::speakText(spatialChat);
}
// ----------------------------------------------------------------------
int CreatureObject::getPerformanceType() const
{
return m_performanceType.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setPerformanceType(int performanceType)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<int> *msg = new MessageQueueGenericValueType<int>(performanceType);
controller->appendMessage(
CM_setPerformanceType,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
m_performanceType = performanceType;
}
}
// ----------------------------------------------------------------------
int CreatureObject::getPerformanceStartTime() const
{
return m_performanceStartTime.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setPerformanceStartTime(int performanceStartTime)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<int> *msg = new MessageQueueGenericValueType<int>(performanceStartTime);
controller->appendMessage(
CM_setPerformanceStartTime,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
m_performanceStartTime = performanceStartTime;
}
}
// ----------------------------------------------------------------------
NetworkId const &CreatureObject::getPerformanceListenTarget() const
{
return m_performanceListenTarget.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setPerformanceListenTarget(NetworkId const &who)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<NetworkId> *msg = new MessageQueueGenericValueType<NetworkId>(who);
controller->appendMessage(
CM_setPerformanceListenTarget,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
m_performanceListenTarget = who;
}
}
// ----------------------------------------------------------------------
NetworkId const &CreatureObject::getPerformanceWatchTarget() const
{
return m_performanceWatchTarget.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setPerformanceWatchTarget(NetworkId const &who)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
MessageQueueGenericValueType<NetworkId> *msg = new MessageQueueGenericValueType<NetworkId>(who);
controller->appendMessage(
CM_setPerformanceWatchTarget,
0.0f,
msg,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
m_performanceWatchTarget = who;
}
}
//-----------------------------------------------------------------------
void CreatureObject::setGuildId(int guildId)
{
FATAL(!isAuthoritative(), ("setGuildId called on nonauthoritative object"));
int oldGuildId = getGuildId();
if (oldGuildId != guildId)
{
if (oldGuildId)
GuildInterface::onAboutToClearGuildId(*this);
m_guildId = guildId;
}
if (guildId)
GuildInterface::onGuildIdSet(*this);
}
//-----------------------------------------------------------------------
void CreatureObject::setTimeToUpdateGuildWarPvpStatus(unsigned long timeToUpdateGuildWarPvpStatus)
{
FATAL(!isAuthoritative(), ("setTimeToUpdateGuildWarPvpStatus called on nonauthoritative object"));
m_timeToUpdateGuildWarPvpStatus = timeToUpdateGuildWarPvpStatus;
}
//-----------------------------------------------------------------------
void CreatureObject::setGuildWarEnabled(bool guildWarEnabled)
{
FATAL(!isAuthoritative(), ("setGuildWarEnabled called on nonauthoritative object"));
m_guildWarEnabled = guildWarEnabled;
m_timeToUpdateGuildWarPvpStatus = 0;
}
// ----------------------------------------------------------------------
int CreatureObject::getMilitiaOfCityId() const
{
return m_militiaOfCityId.get();
}
//-----------------------------------------------------------------------
void CreatureObject::setMilitiaOfCityId(int cityId)
{
FATAL(!isAuthoritative(), ("setMilitiaOfCityId called on nonauthoritative object"));
m_militiaOfCityId = cityId;
}
// ----------------------------------------------------------------------
int CreatureObject::getLocatedInCityId() const
{
return m_locatedInCityId.get();
}
//-----------------------------------------------------------------------
void CreatureObject::setLocatedInCityId(int newCityId)
{
FATAL(!isAuthoritative(), ("setLocatedInCityId called on nonauthoritative object"));
int oldCityId = m_locatedInCityId.get();
if (oldCityId != newCityId)
{
m_locatedInCityId.set(newCityId);
if (getScriptObject())
{
ScriptParams params;
params.addParam(oldCityId);
params.addParam(newCityId);
IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_CITY_CHANGED, params));
}
}
}
// ----------------------------------------------------------------------
NetworkId const &CreatureObject::getMasterId() const
{
return m_masterId.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setMasterId(NetworkId const &masterId)
{
if (isAuthoritative())
m_masterId = masterId;
else
sendControllerMessageToAuthServer(CM_setMasterId, new MessageQueueGenericValueType<NetworkId>(masterId));
}
// ----------------------------------------------------------------------
float CreatureObject::getSwimHeight() const
{
return safe_cast<const SharedCreatureObjectTemplate*>(getSharedTemplate())->getSwimHeight() * getScaleFactor();
}
//-----------------------------------------------------------------------
const CreatureObject::AllCreaturesSet & CreatureObject::getAllCreatures()
{
return g_creatureList;
}
//-----------------------------------------------------------------------
void CreatureObject::addToMissionRequestQueue(const NetworkId & terminalId)
{
if (!ConfigServerGame::getDisableMissions())
{
std::vector<std::pair<CreatureObject *, NetworkId> >::const_iterator f = std::find(gs_missionRequestQueue.begin(), gs_missionRequestQueue.end(), std::make_pair(this, terminalId));
if(f == gs_missionRequestQueue.end())
{
gs_missionRequestQueue.push_back(std::make_pair(this, terminalId));
}
}
}
//-----------------------------------------------------------------------
void CreatureObject::removeFromMissionRequestQueue(const NetworkId & creatureId)
{
CreatureObject * creature = safe_cast<CreatureObject *>(NetworkIdManager::getObjectById(creatureId));
if (creature)
{
removeCreatureFromMissionRequestQueue(creature);
}
}
//-----------------------------------------------------------------------
void CreatureObject::updateMissionRequestQueue()
{
if(! gs_missionRequestQueue.empty())
{
// remove pending creatures from previous run
std::vector<std::pair<CreatureObject *, NetworkId> >::const_iterator remIter;
std::vector<std::pair<CreatureObject *, NetworkId> >::iterator i;
for(remIter = gs_missionRequestQueueDeferredRemoves.begin(); remIter != gs_missionRequestQueueDeferredRemoves.end(); ++remIter)
{
for(i = gs_missionRequestQueue.begin(); i != gs_missionRequestQueue.end(); ++i)
{
if((*i).first == (*remIter).first)
{
gs_missionRequestQueue.erase(i);
break;
}
}
}
gs_missionRequestQueueDeferredRemoves.clear();
int count = 0;
for(i = gs_missionRequestQueue.begin(); i != gs_missionRequestQueue.end(); ++i)
{
CreatureObject * creature = (*i).first;
// if a delete happened while processing the queue, find the
// creature in the defferred deleted queue. If it's there, skip this
// creature and move on to the next
if(! gs_missionRequestQueueDeferredRemoves.empty())
{
// is this creature in the queue?
remIter = std::find(gs_missionRequestQueueDeferredRemoves.begin(), gs_missionRequestQueueDeferredRemoves.end(), std::make_pair(creature, (*i).second));
if(remIter != gs_missionRequestQueueDeferredRemoves.end())
continue;
}
// build vector of network ids for all mission objects in the mission bag
ServerObject * missionBag = creature->getMissionBag();
if(missionBag)
{
static std::vector<NetworkId> missionObjects;
Container * container = ContainerInterface::getContainer(*missionBag);
ContainerIterator iter;
for(iter = container->begin(); iter != container->end(); ++iter)
{
MissionObject * missionObject = safe_cast<MissionObject *>((*iter).getObject());
if(missionObject)
{
missionObjects.push_back(missionObject->getNetworkId());
}
}
ScriptParams p;
p.addParam(creature->getNetworkId());
p.addParam((*i).second);
p.addParam(missionObjects);
GameScriptObject * script = creature->getScriptObject();
if(script)
IGNORE_RETURN(script->trigAllScripts(Scripting::TRIG_PLAYER_REQUEST_MISSION_BOARD, p));
removeCreatureFromMissionRequestQueue(creature);
missionObjects.clear();
}
++count;
if (count >= ConfigServerGame::getMissionRequestsPerFrame())
{
break;
}
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::getAttributes(stdvector<std::pair<std::string, Unicode::String> >::fwd &data) const
{
TangibleObject::getAttributes(data);
}
//-----------------------------------------------------------------------
bool CreatureObject::isAppearanceEquippable(const char *appearanceTemplateName)
{
// Returns whether an object template is wearable based on the data in the
// appearance_table.tab
bool result = true;
// Make sure this object has a valid appearance
if (appearanceTemplateName == NULL)
{
result = false;
}
else
{
// Check to see if we even care about the creature object depending
// on its gender/species combination
SharedCreatureObjectTemplate::Species species = getSpecies();
SharedCreatureObjectTemplate::Gender gender = getGender();
int column = -1;
// Get the species index
switch (species)
{
case SharedCreatureObjectTemplate::SP_human: { column = 1; } break;
case SharedCreatureObjectTemplate::SP_rodian: { column = 3; } break;
case SharedCreatureObjectTemplate::SP_monCalamari: { column = 5; } break;
case SharedCreatureObjectTemplate::SP_wookiee: { column = 7; } break;
case SharedCreatureObjectTemplate::SP_twilek: { column = 9; } break;
case SharedCreatureObjectTemplate::SP_trandoshan: { column = 11; } break;
case SharedCreatureObjectTemplate::SP_zabrak: { column = 13; } break;
case SharedCreatureObjectTemplate::SP_bothan: { column = 15; } break;
case SharedCreatureObjectTemplate::SP_ithorian: { column = 17; } break;
case SharedCreatureObjectTemplate::SP_sullustan: { column = 19; } break;
default: { } break;
}
// Add in the gender index
switch (gender)
{
case SharedCreatureObjectTemplate::GE_male: { } break;
case SharedCreatureObjectTemplate::GE_female: { column += 1; } break;
default: { } break;
}
if (column > 0)
{
// If the object template is in the list, then check if it is equippable, otherwise
// there is no restriction so the item must be equippable
std::string objectTemplateName(appearanceTemplateName);
if (!objectTemplateName.empty())
{
// Strip out just the object template name
size_t const slashIndex = objectTemplateName.rfind('/');
size_t const dotIndex = objectTemplateName.rfind('.');
std::string fileName;
if (slashIndex == std::string::npos)
{
fileName = objectTemplateName.substr(0, dotIndex);
}
else if (dotIndex == std::string::npos)
{
fileName = objectTemplateName.substr(slashIndex + 1);
}
else
{
fileName = objectTemplateName.substr(slashIndex + 1, dotIndex - slashIndex - 1);
}
if (AppearanceManager::isAppearanceManaged(fileName))
{
// A ':block' token in the data table denotes the item is not equippable
std::string destAppearancePath;
AppearanceManager::getAppearanceName(destAppearancePath, fileName, column);
static const std::string tagBlock = ":block";
if (destAppearancePath == tagBlock)
{
result = false;
}
}
}
}
}
return result;
}
// ----------------------------------------------------------------------
/**
* Sends a message to the Planet Server to update the position of the object.
*/
void CreatureObject::updatePlanetServerInternal(const bool forceUpdate) const
{
if (forceUpdate || getPositionChanged())
{
// If this is a mount with a rider on it, use the larger of the rider and mount's
// interest radius for the mount. This allows the planet server to update nodes
// marked for proxy subscription properly. Otherwise we'll get a destroy proxy
// for the mount the first time it moves into a node that hasn't yet been subscribed,
// immediately followed by a create proxy message for the mount when the rider's
// position gets updated.
int effectiveInterestRadius = getInterestRadius();
if (getState(States::MountedCreature))
{
CreatureObject const *const rider = getPrimaryMountingRider();
if (rider)
effectiveInterestRadius = std::max(rider->getInterestRadius(), getInterestRadius());
}
if (!ContainerInterface::getContainedByObject(*this) || (effectiveInterestRadius > 0))
{
bool hibernating = false;
if(getCreatureController())
hibernating = getCreatureController()->getHibernate();
AICreatureController const * const aiCreatureController = dynamic_cast<AICreatureController const *>(getCreatureController());
Object const * const topmostContainer = ContainerInterface::getTopmostContainer(*this);
FATAL(!topmostContainer, ("Object %s was contained by something, but getTopmostContainer() returned NULL", getNetworkId().getValueString().c_str()));
Vector const &position = topmostContainer->getPosition_p();
UpdateObjectOnPlanetMessage const msg(
getNetworkId(),
topmostContainer == this ? NetworkId::cms_invalid : topmostContainer->getNetworkId(),
static_cast<int>(position.x),
static_cast<int>(position.y),
static_cast<int>(position.z),
effectiveInterestRadius,
static_cast<int>(getLocationReservationRadius()),
false,
true,
static_cast<int>(getObjectType()),
static_cast<int>(getLevel()),
hibernating,
getTemplateCrc(),
aiCreatureController ? aiCreatureController->getMovementType() : -1,
getIsStatic() == true ? 1 : 0);
GameServer::getInstance().sendToPlanetServer(msg);
}
}
}
// ----------------------------------------------------------------------
int CreatureObject::getCurrentTargetsTotal(const std::map<Attributes::Enumerator, Attributes::Value> & targets)
{
int sum = 0;
for(std::map<Attributes::Enumerator, Attributes::Value>::const_iterator i = targets.begin(); i != targets.end(); ++i)
{
sum += i->second;
}
return sum;
}
// ======================================================================
ServerObject *CreatureObject::getMissionBag() const
{
SlottedContainer const * const s = ContainerInterface::getSlottedContainer(*this);
if (s)
{
static SlotId const bankId = SlotIdManager::findSlotId(MISSION_BAG_SLOT_NAME);
Container::ContainerErrorCode tmp = Container::CEC_Success;
Object * const o = s->getObjectInSlot(bankId, tmp).getObject();
if (o)
return o->asServerObject();
}
return 0;
}
// ======================================================================
ServerObject *CreatureObject::getBankContainer() const
{
return const_cast<CreatureObject *>(this)->getBank ();
}
// ======================================================================
namespace PlayerBanks
{
const static Unicode::String invalidBankName =
Unicode::narrowToWide("INVALID_BANK_NAME");
}
//----------------------------------------------------------------------
Unicode::String CreatureObject::getBankName() const
{
Unicode::String bankName = PlayerBanks::invalidBankName;
getObjVars().getItem(OBJVAR_BANK_ID,bankName);
return bankName;
}
// ======================================================================
bool CreatureObject::isBankMember(Unicode::String bankName) const
{
return (bankName.compare(getBankName()) == 0);
}
// ======================================================================
bool CreatureObject::joinBank(Unicode::String bankName)
{
if (getBankName() == bankName)
{
StringId message("system_msg", "already_member_of_bank");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
return false;
}
if (getBankName() != PlayerBanks::invalidBankName)
{
StringId message("system_msg", "member_of_different_bank");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
return false;
}
StringId message("system_msg", "succesfully_joined_bank");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
// printf("Setting item (%s) on objvars (%x)\n", OBJVAR_BANK_ID.c_str(), (int)objVars);
setObjVarItem(OBJVAR_BANK_ID, bankName);
/*DynamicVariable *objvar = objVars->getItemByName(OBJVAR_BANK_ID);
if (objvar)
{
DynamicVariableString *stringVar = dynamic_cast<DynamicVariableString *>(objvar);
stringVar->setValue(bankName);
}*/
return true;
}
// ======================================================================
void CreatureObject::quitBank()
{
ServerObject *bankContainer = getBankContainer();
if (bankContainer)
{
Container *container = ContainerInterface::getContainer(*bankContainer);
if (container->getNumberOfItems() != 0)
{
//Can't quit bank, you still have items in it
StringId message("system_msg", "bank_not_empty");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
return;
}
}
StringId message("system_msg", "succesfully_quit_bank");
Unicode::String outOfBand;
Chat::sendSystemMessage(*this, message, outOfBand);
setObjVarItem(OBJVAR_BANK_ID, PlayerBanks::invalidBankName);
/*DynamicVariable *objvar = objVars->getItemByName(OBJVAR_BANK_ID);
if (objvar)
{
DynamicVariableString *stringVar = dynamic_cast<DynamicVariableString *>(objvar);
stringVar->setValue(PlayerBanks::invalidBankName);
}*/
setObjVarItem(OBJVAR_OPEN_BANK_TERMINAL_ID, NetworkId::cms_invalid);
if (bankContainer)
{
ScriptParams params;
params.addParam(getNetworkId());
IGNORE_RETURN (bankContainer->getScriptObject()->trigAllScripts(Scripting::TRIG_CLOSED_CONTAINER, params))
;
Client *client = getClient();
if (client)
{
GenericValueTypeMessage<NetworkId> m("ClosedContainerMessage", bankContainer->getNetworkId());
client->send(m, true);
ObserveTracker::onClientClosedContainer(*client, *bankContainer);
}
}
}
// ----------------------------------------------------------------------
float CreatureObject::getInvulnerabilityTimer() const
{
return m_invulnerabilityTimer.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setInvulnerabilityTimer(float duration)
{
if (isAuthoritative())
{
DEBUG_REPORT_LOG(true, ("Setting creature invulnerability timer for %s to %g\n", getDebugInformation().c_str(), duration));
m_invulnerabilityTimer = duration;
}
}
// ----------------------------------------------------------------------
bool CreatureObject::isInvulnerable() const
{
if (m_invulnerabilityTimer.get() > 0.f)
return true;
return TangibleObject::isInvulnerable();
}
// ----------------------------------------------------------------------
void CreatureObject::handleCMessageTo(MessageToPayload const &message)
{
if (message.getMethod() == "C++RenameFailed")
{
std::string reason(message.getDataAsString());
if (reason.empty())
reason = "Rename request failed.";
Chat::sendSystemMessage(*this,Unicode::narrowToWide(reason),Unicode::String());
}
else if ((message.getMethod() == "C++PlayerRenameRequestSubmitted") || (message.getMethod() == "C++PlayerLastNameRenameRequestSubmitted"))
{
// set objvar to indicate there's a pending rename request for this character,
// and the time of the rename request, to enforce the 90 days wait between rename
if (!getClient() || !getClient()->isGod())
setObjVarItem("renameCharacterRequest.requestTime", static_cast<int>(::time(NULL)));
std::string const newName(message.getDataAsString());
if (getObjVars().hasItem("renameCharacterRequest.requestTime") && !newName.empty())
{
setObjVarItem("renameCharacterRequest.requestNewName", newName);
}
// for player requested rename, send message to CentralServer to drop the character,
// and prevent the character from further login until the rename request has been completed
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
GenericValueTypeMessage<std::pair<unsigned int, std::pair<NetworkId, std::pair<std::string, bool> > > > const msg("PlayerRenameRequestSubmitted", std::make_pair(static_cast<unsigned int>(playerObject->getStationId()), std::make_pair(getNetworkId(), std::make_pair(newName, (message.getMethod() == "C++PlayerLastNameRenameRequestSubmitted")))));
GameServer::getInstance().sendToCentralServer(msg);
}
}
else if (message.getMethod() == "C++GmRenameRequestSubmitted")
{
std::string reason(message.getDataAsString());
if (reason.empty())
reason = "Rename request submitted.";
Chat::sendSystemMessage(*this,Unicode::narrowToWide(reason),Unicode::String());
}
else if (message.getMethod() == "C++experience")
{
std::vector<int8> const & packedData = message.getPackedDataVector();
std::vector<int8>::const_iterator spacePos = std::find(packedData.begin(), packedData.end(), ' ');
if (spacePos != packedData.end())
{
int amount = atoi(std::string(spacePos + 1, packedData.end()).c_str());
grantExperiencePoints(std::string(packedData.begin(), spacePos), amount);
}
}
else if (message.getMethod() == "C++AddJediSlot")
{
// if we haven't received an addJediToAccountAck(), resend the Jedi request
if (!getObjVars().hasItem(OBJVAR_ADD_JEDI_ACK))
{
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL)
{
player->addJediToAccount();
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++AddJediSlot", "", 5 * 60, true);
}
}
else
{
removeObjVarItem(OBJVAR_ADD_JEDI_ACK);
}
}
else if (message.getMethod() == "ClientMfdStatusUpdateMessage")
{
if (getClient() && isAuthoritative())
{
if (!message.getPackedDataVector().empty())
{
static Archive::ByteStream bs;
bs.clear();
bs.put((&(message.getPackedDataVector()[0])), message.getPackedDataVector().size());
Archive::put(bs, message.getPackedDataVector());
Archive::ReadIterator ri = bs.begin();
ClientMfdStatusUpdateMessage mfdUpdate(ri);
getClient()->send(mfdUpdate, false);
}
}
}
else if (message.getMethod() == "FindFriend")
{
if (getClient() && isAuthoritative())
{
if (!message.getPackedDataVector().empty())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
static Archive::ByteStream bs;
bs.clear();
bs.put((&(message.getPackedDataVector()[0])), message.getPackedDataVector().size());
Archive::put(bs, message.getPackedDataVector());
Archive::ReadIterator ri = bs.begin();
NetworkId source;
std::string sourceName;
Archive::get(ri, source);
Archive::get(ri, sourceName);
playerObject->replyLocationToFriend(sourceName, source);
}
}
}
}
else if (message.getMethod() == "LocationToFriend")
{
if (getClient() && isAuthoritative())
{
if (!message.getPackedDataVector().empty())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
static Archive::ByteStream bs;
bs.clear();
bs.put((&(message.getPackedDataVector()[0])), message.getPackedDataVector().size());
Archive::put(bs, message.getPackedDataVector());
Archive::ReadIterator ri = bs.begin();
Location friendLocation;
NetworkId friendId;
Archive::get(ri, friendLocation);
Archive::get(ri, friendId);
playerObject->receiveLocationToFriend(friendId, friendLocation);
}
}
}
}
else if (message.getMethod() == "FailLocationToFriend")
{
if (getClient() && isAuthoritative())
{
if (!message.getPackedDataVector().empty())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
static Archive::ByteStream bs;
bs.clear();
bs.put((&(message.getPackedDataVector()[0])), message.getPackedDataVector().size());
Archive::put(bs, message.getPackedDataVector());
Archive::ReadIterator ri = bs.begin();
NetworkId friendId;
Archive::get(ri, friendId);
playerObject->receiveFailLocationToFriend(friendId);
}
}
}
}
else if (message.getMethod() == "C++TheaterDestroyed")
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
playerObject->handleCMessageTo(message);
}
}
else if (message.getMethod() == "C++RemovePetFromGroup")
{
if (!message.getPackedDataVector().empty())
{
std::string packedData(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
// Parse the info from the message
char petNetworkIdBuffer[100];
unsigned msgSendCount;
sscanf(packedData.c_str(), "%s %u", petNetworkIdBuffer, &msgSendCount);
// Turn the string into a network Id
NetworkId petNetworkId( petNetworkIdBuffer );
// Try to remove the pet from the group
GroupObject *group = getGroup();
if (group && group->isGroupMember(petNetworkId))
{
group->removeGroupMember(petNetworkId);
}
else
{
// We will only try to remove the pet so many times...
if (msgSendCount < MAX_ATTEMPTS_TO_REMOVE_PET_FROM_GROUP)
{
// We will try again in the future...
++msgSendCount;
// We need to tell the network ID of the pet and we need to keep
// of how many times this message has been sent to the inviter
char msgBuffer[100];
sprintf( msgBuffer, "%s %u", petNetworkId.getValueString().c_str(), msgSendCount );
// Send the message with a delay
MessageToQueue::getInstance().sendMessageToC( getNetworkId(),
"C++RemovePetFromGroup",
msgBuffer,
TIME_BETWEEN_ATTEMPTS_TO_REMOVE_PET,
false ); // reliable
}
}
}
}
else if (message.getMethod() == "C++WaitForPatrolPreload")
{
if (ServerWorld::isPreloadComplete())
{
DEBUG_REPORT_LOG(ConfigServerGame::isAiLoggingEnabled(),("C++WaitForPatrolPreload being handled for %s\n", getNetworkId().getValueString().c_str()));
MessageToQueue::cancelRecurringMessageTo(getNetworkId(), "C++WaitForPatrolPreload");
AICreatureController * const aiCreatureController = safe_cast<AICreatureController * const>(getController());
if (aiCreatureController != NULL)
{
const std::string data = message.getDataAsString();
std::string::size_type locationStart = 0;
std::string::size_type locationEnd = data.find('|', locationStart);
std::vector<Unicode::String> locations;
while (locationEnd != std::string::npos)
{
locations.push_back(Unicode::narrowToWide(data.substr(locationStart, locationEnd - locationStart)));
locationStart = locationEnd + 1;
locationEnd = data.find('|', locationStart);
}
bool random = false;
bool flip = false;
bool repeat = false;
if (data.size() > (locationStart+2))
{
random = data.at(locationStart++) != '0';
flip = data.at(locationStart++) != '0';
repeat = data.at(locationStart++) != '0';
}
int startPoint = 0;
if (data.size() > locationStart && data.at(locationStart) == '*')
{
++locationStart;
startPoint = atoi(data.substr(locationStart).c_str());
}
aiCreatureController->patrol(locations, random, flip, repeat, startPoint);
}
}
}
else if (message.getMethod() == "C++WarnAboutVendorPurge")
{
LOG("CustomerService",("Purge: Sending warning emails to the owner (%s) of vendor (%s) effected by a purge",getNetworkId().getValueString().c_str(),message.getDataAsString().c_str()));
const StringId subject("player_structure", "vendor_purge_warning_subject");
const StringId body("player_structure", "vendor_purge_warning_body");
const Unicode::String mail_subject = Unicode::narrowToWide("@" + subject.getCanonicalRepresentation());
const Unicode::String mail_body = Unicode::narrowToWide("@" + body.getCanonicalRepresentation());
ProsePackage pp;
pp.stringId = body;
pp.other.str = Unicode::narrowToWide(message.getDataAsString());
Unicode::String oob;
OutOfBandPackager::pack(pp, -1, oob);
Chat::sendPersistentMessage("Galactic Housing Authority", Unicode::wideToNarrow(getObjectName()),
mail_subject, Unicode::emptyString, oob);
}
else if (message.getMethod() == "C++CheckCharacterTitle")
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject && !playerObject->getTitle().empty())
{
static uint32 const commandHash = Crc::normalizeAndCalculate("setCurrentSkillTitle");
const Command &command = CommandTable::getCommand(static_cast<uint32>(commandHash));
if (!command.isNull())
{
commandQueueEnqueue(command, NetworkId::cms_invalid, Unicode::narrowToWide(playerObject->getTitle()), 0, false);
}
}
}
else if (message.getMethod() == "C++CharacterSheetInfoResidenceLocationRsp")
{
if (!message.getPackedDataVector().empty())
{
// location of residence
std::string const residenceLoc(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
// get the city(ies) this character is a citizen of
std::string citizensOf;
std::vector<int> const & cityId = CityInterface::getCitizenOfCityId(getNetworkId());
if (!cityId.empty())
{
std::string cityName;
for (std::vector<int>::const_iterator iterCity = cityId.begin(); iterCity != cityId.end(); ++iterCity)
{
CityInfo const & cityInfo = CityInterface::getCityInfo(*iterCity);
cityName = cityInfo.getCityName();
if (cityName.empty())
continue;
if (!citizensOf.empty())
citizensOf += ",";
if (getNetworkId() == cityInfo.getLeaderId())
citizensOf += "*";
citizensOf += cityName;
}
}
// send to client
GenericValueTypeMessage<std::pair<std::string, std::string> > message("CharacterSheetResponseResLoc", std::make_pair(residenceLoc, citizensOf));
Client * const client = getClient();
if (client)
client->send(message, true);
}
}
else if (message.getMethod() == "C++SpammerReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
std::string::size_type const pos = params.find(' ');
if ((pos != std::string::npos) && (params.size() > (pos + 1)))
{
NetworkId const actor(params.substr(0, pos));
std::string const actorName(params.substr(pos + 1));
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
bool canSpammer = true;
// on a session-authenticated cluster, cannot /spammer a secured login character
if (getClient() && getClient()->isGodValidated())
{
if (!ConfigServerGame::getAdminGodToAll() && !ConfigServerGame::getEnableWardenCanSquelchSecuredLogin())
{
canSpammer = false;
}
}
// cannot /spammer self
if (canSpammer && (actor == getNetworkId()))
{
canSpammer = false;
}
// cannot /spammer a target that is already squelched
if (canSpammer && (playerObject->getSecondsUntilUnsquelched() != 0))
{
canSpammer = false;
}
if (!canSpammer)
{
MessageToQueue::getInstance().sendMessageToC(actor,
"C++SpammerRspCannotSpammerTarget",
Unicode::wideToNarrow(getAssignedObjectName()),
0,
false);
}
else
{
// do it
playerObject->squelch(actor, actorName, ConfigServerGame::getWardenSquelchDurationSeconds());
std::string result = getNetworkId().getValueString();
result += "|";
result += Unicode::wideToNarrow(getAssignedObjectName());
result += "|";
result += playerObject->getAccountDescription();
MessageToQueue::getInstance().sendMessageToC(actor,
"C++SpammerRspSuccess",
result,
0,
false);
}
}
else
{
MessageToQueue::getInstance().sendMessageToC(actor,
"C++SpammerRspInvalidTarget",
std::string(),
0,
false);
}
}
}
}
else if (message.getMethod() == "C++SpammerRspTargetNotFound")
{
Chat::sendSystemMessage(*this, StringId("warden", "invalid_target"), Unicode::emptyString);
}
else if (message.getMethod() == "C++SpammerRspInvalidTarget")
{
Chat::sendSystemMessage(*this, StringId("warden", "invalid_target"), Unicode::emptyString);
}
else if (message.getMethod() == "C++SpammerRspCannotSpammerTarget")
{
if (!message.getPackedDataVector().empty())
{
std::string const targetName(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "cannot_spammer_target");
prosePackage.target.str = Unicode::narrowToWide(targetName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
else if (message.getMethod() == "C++SpammerRspSuccess")
{
if (!message.getPackedDataVector().empty())
{
std::string const result(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
std::string::size_type const pos1 = result.find('|');
if ((pos1 != std::string::npos) && (result.size() > (pos1 + 1)))
{
std::string::size_type const pos2 = result.find('|', (pos1 + 1));
if ((pos2 != std::string::npos) && (result.size() > (pos2 + 1)))
{
NetworkId const target(result.substr(0, pos1));
std::string const targetName(result.substr(pos1 + 1, pos2 - pos1 - 1));
std::string const targetAccountDescription(result.substr(pos2 + 1));
// request /report
ReportManager::addReport(getAssignedObjectFirstName(), getNetworkId(), Unicode::narrowToWide(NameManager::normalizeName(targetName)), target, std::string("Warden"));
// log it
LOG("CustomerService", ("Warden:%s has spammer %s", PlayerObject::getAccountDescription(this).c_str(), targetAccountDescription.c_str()));
// display confirmation message
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "confirm_spammer");
prosePackage.target.str = Unicode::narrowToWide(targetName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
}
}
else if (message.getMethod() == "C++UnspammerReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
NetworkId const actor(params);
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
bool canUnspammer = true;
// cannot /unspammer a target that is not currently /spammer(ed)
if (playerObject->getSecondsUntilUnsquelched() <= 0)
{
canUnspammer = false;
}
// can only /unspammer target that was /spammer(ed) by the actor
if (canUnspammer && (playerObject->getSquelchedById() != actor))
{
canUnspammer = false;
}
if (!canUnspammer)
{
MessageToQueue::getInstance().sendMessageToC(actor,
"C++UnspammerRspCannotUnspammerTarget",
Unicode::wideToNarrow(getAssignedObjectName()),
0,
false);
}
else
{
// do it
playerObject->unsquelch();
std::string result = Unicode::wideToNarrow(getAssignedObjectName());
result += "|";
result += playerObject->getAccountDescription();
MessageToQueue::getInstance().sendMessageToC(actor,
"C++UnspammerRspSuccess",
result,
0,
false);
}
}
else
{
MessageToQueue::getInstance().sendMessageToC(actor,
"C++UnspammerRspInvalidTarget",
std::string(),
0,
false);
}
}
}
else if (message.getMethod() == "C++UnspammerRspTargetNotFound")
{
Chat::sendSystemMessage(*this, StringId("warden", "invalid_target"), Unicode::emptyString);
}
else if (message.getMethod() == "C++UnspammerRspInvalidTarget")
{
Chat::sendSystemMessage(*this, StringId("warden", "invalid_target"), Unicode::emptyString);
}
else if (message.getMethod() == "C++UnspammerRspCannotUnspammerTarget")
{
if (!message.getPackedDataVector().empty())
{
std::string const targetName(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "cannot_unspammer_target");
prosePackage.target.str = Unicode::narrowToWide(targetName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
else if (message.getMethod() == "C++UnspammerRspSuccess")
{
if (!message.getPackedDataVector().empty())
{
std::string const result(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
std::string::size_type const pos = result.find('|');
if ((pos != std::string::npos) && (result.size() > (pos + 1)))
{
std::string const targetName(result.substr(0, pos));
std::string const targetAccountDescription(result.substr(pos + 1));
// log it
LOG("CustomerService", ("Warden:%s has unspammer %s", PlayerObject::getAccountDescription(this).c_str(), targetAccountDescription.c_str()));
// display confirmation message
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "confirm_unspammer");
prosePackage.target.str = Unicode::narrowToWide(targetName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
}
else if (message.getMethod() == "C++DeputizeWardenReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
NetworkId existingWarden;
StationId existingWardenStationId;
bool success = false;
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 2))
{
existingWarden = NetworkId(Unicode::wideToNarrow(tokens[0]));
// quick way to convert a string to a uint32
existingWardenStationId = static_cast<StationId>(NetworkId(Unicode::wideToNarrow(tokens[1])).getValue());
if (existingWarden != getNetworkId())
{
PlayerObject * po = PlayerCreatureController::getPlayerObject(this);
if (po && !po->isWarden() && (po->getStationId() == existingWardenStationId))
{
// make warden
success = true;
po->setPriviledgedTitle(static_cast<int8>(PlayerDataPriviledgedTitle::Warden));
// send success message back to existing warden who deputized this warden
std::string result = Unicode::wideToNarrow(getAssignedObjectName());
result += "|";
result += po->getAccountDescription();
MessageToQueue::getInstance().sendMessageToC(existingWarden,
"C++DeputizeWardenRspSuccess",
result,
0,
false);
}
}
}
if (!success)
{
MessageToQueue::getInstance().sendMessageToC(existingWarden,
"C++DeputizeWardenRspCannotDeputize",
std::string(),
0,
false);
}
}
}
else if (message.getMethod() == "C++DeputizeWardenRspSuccess")
{
if (!message.getPackedDataVector().empty())
{
std::string const result(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
std::string::size_type const pos = result.find('|');
if ((pos != std::string::npos) && (result.size() > (pos + 1)))
{
std::string const newWardenName(result.substr(0, pos));
std::string const newWardenAccountInfo(result.substr(pos + 1));
// log it
LOG("CustomerService", ("WardenPlayerDeputize:%s has deputized %s", PlayerObject::getAccountDescription(this).c_str(), newWardenAccountInfo.c_str()));
// display confirmation message
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "confirm_deputize");
prosePackage.target.str = Unicode::narrowToWide(newWardenName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
}
else if (message.getMethod() == "C++DeputizeWardenRspCannotDeputize")
{
Chat::sendSystemMessage(*this, StringId("warden", "cannot_deputize"), Unicode::emptyString);
}
else if (message.getMethod() == "C++UndeputizeWardenReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
NetworkId existingWarden;
StationId existingWardenStationId;
bool success = false;
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 2))
{
existingWarden = NetworkId(Unicode::wideToNarrow(tokens[0]));
// quick way to convert a string to a uint32
existingWardenStationId = static_cast<StationId>(NetworkId(Unicode::wideToNarrow(tokens[1])).getValue());
if (existingWarden != getNetworkId())
{
PlayerObject * po = PlayerCreatureController::getPlayerObject(this);
if (po && po->isWarden() && (po->getStationId() == existingWardenStationId))
{
// revoke warden
success = true;
po->setPriviledgedTitle(static_cast<int8>(PlayerDataPriviledgedTitle::NormalPlayer));
// send success message back to existing warden who undeputized this warden
std::string result = Unicode::wideToNarrow(getAssignedObjectName());
result += "|";
result += po->getAccountDescription();
MessageToQueue::getInstance().sendMessageToC(existingWarden,
"C++UndeputizeWardenRspSuccess",
result,
0,
false);
}
}
}
if (!success)
{
MessageToQueue::getInstance().sendMessageToC(existingWarden,
"C++UndeputizeWardenRspCannotUndeputize",
std::string(),
0,
false);
}
}
}
else if (message.getMethod() == "C++UndeputizeWardenRspSuccess")
{
if (!message.getPackedDataVector().empty())
{
std::string const result(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
std::string::size_type const pos = result.find('|');
if ((pos != std::string::npos) && (result.size() > (pos + 1)))
{
std::string const priorWardenName(result.substr(0, pos));
std::string const pirorWardenAccountInfo(result.substr(pos + 1));
// log it
LOG("CustomerService", ("WardenPlayerUndeputize:%s has undeputized %s", PlayerObject::getAccountDescription(this).c_str(), pirorWardenAccountInfo.c_str()));
// display confirmation message
ProsePackage prosePackage;
prosePackage.stringId = StringId("warden", "confirm_undeputize");
prosePackage.target.str = Unicode::narrowToWide(priorWardenName);
Chat::sendSystemMessage(*this, prosePackage);
}
}
}
else if (message.getMethod() == "C++UndeputizeWardenRspCannotUndeputize")
{
Chat::sendSystemMessage(*this, StringId("warden", "cannot_undeputize"), Unicode::emptyString);
}
else if (message.getMethod() == "C++OnCollectionServerFirst")
{
if (!message.getPackedDataVector().empty())
{
std::string const collectionName(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
CollectionsDataTable::CollectionInfoCollection const * const collectionInfo = CollectionsDataTable::getCollectionByName(collectionName);
if (collectionInfo && collectionInfo->trackServerFirst)
{
GameScriptObject * gameScriptObject = const_cast<GameScriptObject *>(getScriptObject());
if (gameScriptObject)
{
ScriptParams params;
params.addParam(collectionInfo->page.book.name.c_str()); // book
params.addParam(collectionInfo->page.name.c_str()); // page
params.addParam(collectionInfo->name.c_str()); // collection
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_COLLECTION_SERVER_FIRST, params));
}
}
}
}
else if (message.getMethod() == "C++InviteToGroupReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
bool success = false;
NetworkId actor, actorShip, group;
std::string actorName;
GroupObject const * groupObject = NULL;
StringId responseSid;
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 4))
{
success = true;
actor = NetworkId(Unicode::wideToNarrow(tokens[0]));
actorShip = NetworkId(Unicode::wideToNarrow(tokens[1]));
group = NetworkId(Unicode::wideToNarrow(tokens[2]));
actorName = Unicode::wideToNarrow(tokens[3]);
ServerObject const * so = (group.isValid() ? safe_cast<ServerObject const *>(NetworkIdManager::getObjectById(group)) : NULL);
groupObject = (so ? so->asGroupObject() : NULL);
}
if (success)
{
std::vector<CreatureObject *> targets;
GroupHelpers::findAllTargetsForGroup(this, targets);
if (!GroupHelpers::roomInGroup(groupObject, targets.size()))
{
responseSid = GroupStringId::SID_GROUP_FULL;
success = false;
}
}
if (success)
{
if ((this->getGroup()) && (!GroupHelpers::creatureIsContainedInPOBShip(this)))
{
responseSid = GroupStringId::SID_GROUP_ALREADY_GROUPED;
success = false;
}
}
if (success)
{
GameScriptObject* const gso = getScriptObject();
if (gso != NULL && gso->hasScript("ai.beast"))
{
responseSid = GroupStringId::SID_GROUP_BEASTS_CANT_JOIN;
success = false;
}
}
if (success)
{
NetworkId const & oldInviter = getGroupInviterId();
if (oldInviter.isValid())
{
if (oldInviter == actor)
responseSid = GroupStringId::SID_GROUP_CONSIDERING_YOUR_GROUP;
else
responseSid = GroupStringId::SID_GROUP_CONSIDERING_OTHER_GROUP;
success = false;
}
}
if (success)
{
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject && playerObject->isIgnoring(Unicode::toLower(actorName)))
{
responseSid = GroupStringId::SID_GROUP_INVITE_NO_TARGET_SELF;
success = false;
}
}
if (success)
{
// cannot be invited if in combat
if (getState(States::Combat) && ConfigServerGame::getGroupInviteInviteeCombatRestriction())
{
responseSid = GroupStringId::SID_GROUP_INVITE_TARGET_IN_COMBAT;
success = false;
}
}
if (success)
{
ProsePackage pp;
pp.stringId = GroupStringId::SID_GROUP_INVITE_TARGET;
pp.target.str = Unicode::narrowToWide(actorName);
pp.actor.id = getNetworkId();
Chat::sendSystemMessage(*this, pp);
setGroupInviter(actor, actorName, actorShip);
responseSid = GroupStringId::SID_GROUP_INVITE_LEADER;
}
std::string const objectName(Unicode::wideToNarrow(getAssignedObjectName()));
std::string response = responseSid.getCanonicalRepresentation();
response += "|";
if (!objectName.empty())
{
response += "nameString|";
response += objectName;
}
else
{
StringId nameSid = getObjectNameStringId();
response += "nameStringId|";
response += nameSid.getCanonicalRepresentation();
}
MessageToQueue::getInstance().sendMessageToC(actor,
"C++GroupOperationGenericRsp",
response,
0,
false);
}
}
else if (message.getMethod() == "C++InviteToGroupRspTargetNotFound")
{
Chat::sendSystemMessageSimple(*this, GroupStringId::SID_GROUP_INVITE_NO_TARGET_SELF, NULL);
}
else if (message.getMethod() == "C++GroupOperationGenericRsp")
{
if (!message.getPackedDataVector().empty())
{
std::string const result(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(result), tokens, &delimiters, NULL)) && (tokens.size() == 3))
{
std::string const response(Unicode::wideToNarrow(tokens[0]));
std::string const responseParmType(Unicode::wideToNarrow(tokens[1]));
std::string const responseParm(Unicode::wideToNarrow(tokens[2]));
ProsePackage pp;
pp.stringId = StringId(response);
if (responseParmType == std::string("nameString"))
{
pp.target.str = Unicode::narrowToWide(responseParm);
}
else
{
pp.target.stringId = StringId(responseParm);
}
pp.actor.id = getNetworkId();
Chat::sendSystemMessage(*this, pp);
}
}
}
else if (message.getMethod() == "C++UninviteFromGroupReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
bool success = false;
NetworkId actor;
std::string actorName;
StringId responseSid;
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 2))
{
success = true;
actor = NetworkId(Unicode::wideToNarrow(tokens[0]));
actorName = Unicode::wideToNarrow(tokens[1]);
}
if (success)
{
if (actor != getGroupInviterId())
{
responseSid = GroupStringId::SID_GROUP_UNINVITE_NOT_INVITED;
success = false;
}
}
if (success)
{
ProsePackage pp;
pp.stringId = GroupStringId::SID_GROUP_UNINVITE_TARGET;
pp.target.str = Unicode::narrowToWide(actorName);
pp.actor.id = getNetworkId();
Chat::sendSystemMessage(*this, pp);
setGroupInviter(NetworkId::cms_invalid, std::string(), NetworkId::cms_invalid);
responseSid = GroupStringId::SID_GROUP_UNINVITE_SELF;
}
std::string const objectName(Unicode::wideToNarrow(getAssignedObjectName()));
std::string response = responseSid.getCanonicalRepresentation();
response += "|";
if (!objectName.empty())
{
response += "nameString|";
response += objectName;
}
else
{
StringId nameSid = getObjectNameStringId();
response += "nameStringId|";
response += nameSid.getCanonicalRepresentation();
}
MessageToQueue::getInstance().sendMessageToC(actor,
"C++GroupOperationGenericRsp",
response,
0,
false);
}
}
else if (message.getMethod() == "C++UninviteFromGroupRspTargetNotFound")
{
Chat::sendSystemMessageSimple(*this, GroupStringId::SID_GROUP_UNINVITE_NO_TARGET_SELF, NULL);
}
else if (message.getMethod() == "C++GroupJoinInviterInfoReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
NetworkId const invitee(params);
GroupObject const * const existingGroup = getGroup();
GroupMemberParam gmp = GroupHelpers::buildGroupMemberParam(this);
char buffer[1024];
snprintf(buffer, sizeof(buffer)-1, "%s|%s|%s|%d|%d|%d|%s|%d|%d|%d",
(existingGroup ? existingGroup->getNetworkId().getValueString().c_str() : "0"),
gmp.m_memberId.getValueString().c_str(),
gmp.m_memberName.c_str(),
gmp.m_memberDifficulty,
static_cast<int>(gmp.m_memberProfession),
(gmp.m_memberIsPC ? 1 : 0),
gmp.m_memberShipId.getValueString().c_str(),
(gmp.m_memberShipIsPOB ? 1 : 0),
(gmp.m_memberOwnsPOB ? 1 : 0),
(getState(States::Combat) ? 1 : 0));
buffer[sizeof(buffer)-1] = '\0';
MessageToQueue::getInstance().sendMessageToC(invitee,
"C++GroupJoinInviterInfoRsp",
buffer,
0,
false);
// if the inviter had been invited in the meantime, have them decline
if (getGroupInviterId().isValid())
setGroupInviter(NetworkId::cms_invalid, std::string(), NetworkId::cms_invalid);
}
}
else if (message.getMethod() == "C++GroupJoinInviterInfoRsp")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
bool success = false;
NetworkId existingGroupId;
NetworkId inviterId;
std::string inviterName;
int inviterDifficulty = 1;
LfgCharacterData::Profession inviterProfession = LfgCharacterData::Prof_Unknown;
bool inviterIsPC = true;
NetworkId inviterShipId;
bool inviterShipIsPOB = false;
bool inviterOwnsPOB = false;
bool inviterInCombat = false;
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 10))
{
success = true;
existingGroupId = NetworkId(Unicode::wideToNarrow(tokens[0]));
inviterId = NetworkId(Unicode::wideToNarrow(tokens[1]));
inviterName = Unicode::wideToNarrow(tokens[2]);
inviterDifficulty = atoi(Unicode::wideToNarrow(tokens[3]).c_str());
inviterProfession = static_cast<LfgCharacterData::Profession>(atoi(Unicode::wideToNarrow(tokens[4]).c_str()));
inviterIsPC = (atoi(Unicode::wideToNarrow(tokens[5]).c_str()) != 0);
inviterShipId = NetworkId(Unicode::wideToNarrow(tokens[6]));
inviterShipIsPOB = (atoi(Unicode::wideToNarrow(tokens[7]).c_str()) != 0);
inviterOwnsPOB = (atoi(Unicode::wideToNarrow(tokens[8]).c_str()) != 0);
inviterInCombat = (atoi(Unicode::wideToNarrow(tokens[9]).c_str()) != 0);
}
GroupObject * existingGroup = NULL;
if (success)
{
if (existingGroupId.isValid())
{
ServerObject * so = safe_cast<ServerObject *>(NetworkIdManager::getObjectById(existingGroupId));
existingGroup = (so ? so->asGroupObject() : NULL);
if (!existingGroup)
{
success = false;
Chat::sendSystemMessageSimple(*this, GroupStringId::SID_GROUP_EXISTING_GROUP_NOT_FOUND, NULL);
}
}
}
if (success)
{
// make sure inviter hasn't joined another group since the invite
if (existingGroup && (inviterId != existingGroup->getGroupLeaderId()))
{
success = false;
ProsePackage pp;
pp.stringId = GroupStringId::SID_GROUP_JOIN_INVITER_NOT_LEADER;
pp.target.str = Unicode::narrowToWide(inviterName);
pp.actor.id = getNetworkId();
Chat::sendSystemMessage(*this, pp);
}
}
if (success)
{
// cannot join if the inviter is in combat
if (inviterInCombat && ConfigServerGame::getGroupJoinInviterCombatRestriction())
{
success = false;
ProsePackage pp;
pp.stringId = GroupStringId::SID_GROUP_JOIN_LEADER_IN_COMBAT;
pp.target.str = Unicode::narrowToWide(inviterName);
pp.actor.id = getNetworkId();
Chat::sendSystemMessage(*this, pp);
}
}
std::vector<CreatureObject *> targets;
if (success)
{
GroupHelpers::findAllTargetsForGroup(this, targets);
if (existingGroup && !GroupHelpers::roomInGroup(existingGroup, targets.size()))
{
success = false;
Chat::sendSystemMessageSimple(*this, GroupStringId::SID_GROUP_JOIN_FULL, NULL);
}
}
if (success)
{
// if we are in a POB group then we need to disband it
GroupObject * const groupFromPOB = getGroup();
if (groupFromPOB != 0)
{
groupFromPOB->disbandGroup();
std::vector<CreatureObject *>::const_iterator ii = targets.begin();
std::vector<CreatureObject *>::const_iterator iiEnd = targets.end();
for (; ii != iiEnd; ++ii)
{
if ((*ii)->getGroup() == groupFromPOB)
(*ii)->getGroup()->onGroupMemberRemoved((*ii)->getNetworkId(), true);
}
if (getGroup() == groupFromPOB)
getGroup()->onGroupMemberRemoved(getNetworkId(), true);
}
if (existingGroup)
{
GroupObject::GroupMemberParamVector targetMemberParams;
GroupHelpers::buildGroupMemberParamsFromCreatures(targets, targetMemberParams);
int const numberOfTargets = static_cast<int>(targetMemberParams.size());
for (int i = 0; i < numberOfTargets; ++i)
{
existingGroup->addGroupMember(targetMemberParams[i]);
}
}
else
{
// if the inviter happens to be in the same ship as you then we need to remove
// him from the targets list so that he isn't added to the new group twice
for (std::vector<CreatureObject *>::iterator ii = targets.begin(); ii != targets.end(); ++ii)
{
if ((*ii)->getNetworkId() == inviterId)
{
targets.erase(ii);
break;
}
}
GroupObject::GroupMemberParamVector targetMemberParams;
GroupHelpers::buildGroupMemberParamsFromCreatures(targets, targetMemberParams);
// group did not already exist, so form it with the inviter as the leader
if (!targetMemberParams.empty())
ServerUniverse::getInstance().createGroup(GroupMemberParam(inviterId, inviterName, inviterDifficulty, inviterProfession, inviterIsPC, inviterShipId, inviterShipIsPOB, inviterOwnsPOB), targetMemberParams);
}
// Until the targets are actually told they are in a group
// (the authoritative group object may be on another server)
// we will keep track of who extended the invitation
std::vector<CreatureObject *>::const_iterator ii = targets.begin();
std::vector<CreatureObject *>::const_iterator iiEnd = targets.end();
for (; ii != iiEnd; ++ii)
{
(*ii)->setInviterForPendingGroup(inviterId);
}
}
}
}
else if (message.getMethod() == "C++GroupJoinInviterInfoReqInviterNotFound")
{
Chat::sendSystemMessageSimple(*this, GroupStringId::SID_GROUP_MUST_BE_INVITED, NULL);
}
else if (message.getMethod() == "C++LeaveGroupReq")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
GroupObject const * const groupObj = getGroup();
if (groupObj && (groupObj->getNetworkId() == NetworkId(params)) && (groupObj->getGroupLeaderId() != getNetworkId()))
{
static uint32 const commandHash = Crc::normalizeAndCalculate("leaveGroup");
const Command &command = CommandTable::getCommand(static_cast<uint32>(commandHash));
if (!command.isNull())
{
commandQueueEnqueue(command, NetworkId::cms_invalid, Unicode::String(), 0, false, static_cast<Command::Priority>(Command::CP_Front), true);
}
}
}
}
else if (message.getMethod() == "C++GroupPickupPointCreated")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
Unicode::String const delimiters(Unicode::narrowToWide("|"));
Unicode::UnicodeStringVector tokens;
if ((Unicode::tokenize(Unicode::narrowToWide(params), tokens, &delimiters, NULL)) && (tokens.size() == 5))
{
// tell group member that the group pickup point has been created
if (getClient())
{
StringId::LocUnicodeString response;
if (StringId("group", "create_group_pickup_success_others").localize(response))
{
ConsoleMgr::broadcastString(FormattedString<2048>().sprintf(Unicode::wideToNarrow(response).c_str(), Unicode::wideToNarrow(tokens[4]).c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(ConfigServerGame::getGroupPickupPointTimeLimitSeconds())).c_str()),
getClient());
}
}
// create/update the group member's group pickup point waypoint
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
std::string const planetName = Unicode::wideToNarrow(tokens[0]);
int const x = atoi(Unicode::wideToNarrow(tokens[1]).c_str());
int const y = atoi(Unicode::wideToNarrow(tokens[2]).c_str());
int const z = atoi(Unicode::wideToNarrow(tokens[3]).c_str());
Location const location(Vector(static_cast<real>(x), static_cast<real>(y), static_cast<real>(z)), NetworkId::cms_invalid, Location::getCrcBySceneName(planetName));
playerObject->createOrUpdateReusableWaypoint(location, "groupPickupWp", Unicode::narrowToWide("Group Pickup Point"), Waypoint::White);
}
}
}
}
else if (message.getMethod() == "C++OccupyUnlockedSlotRsp")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
LoginUpgradeAccountMessage::OccupyUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::OccupyUnlockedSlotResponse>(::atoi(params.c_str()));
if (response == LoginUpgradeAccountMessage::OUSR_success)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request SUCCESS", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "occupy_unlocked_slot_success"), Unicode::emptyString);
// let the client know so it can change the character to be properly displayed on the character select screen
Client * const client = getClient();
if (client)
{
GenericValueTypeMessage<std::pair<std::string, NetworkId> > const characterChangedUnlocked("CharacterChangedUnlocked", std::make_pair(GameServer::getInstance().getClusterName(), getNetworkId()));
client->send(characterChangedUnlocked, true);
}
}
else if (response == LoginUpgradeAccountMessage::OUSR_db_error)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request FAILED - internal db error", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "occupy_unlocked_slot_db_error"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::OUSR_account_has_no_unlocked_slot)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request FAILED - account doesn't have an unlocked slot", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "occupy_unlocked_slot_db_no_unlocked_slot"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::OUSR_account_has_no_unoccupied_unlocked_slot)
{
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request FAILED - account has no unoccupied unlocked slot", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "occupy_unlocked_slot_db_no_unoccupied_unlocked_slot"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::OUSR_cluster_already_has_unlocked_slot_character)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request FAILED - cluster already has an unlocked slot character", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "occupy_unlocked_slot_db_has_unlocked_slot"), Unicode::emptyString);
}
else
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s OccupyUnlockedSlot request FAILED - unknown result code (%d)", PlayerObject::getAccountDescription(this).c_str(), static_cast<int>(response)));
}
}
}
else if (message.getMethod() == "C++VacateUnlockedSlotRsp")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
LoginUpgradeAccountMessage::VacateUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::VacateUnlockedSlotResponse>(::atoi(params.c_str()));
if (response == LoginUpgradeAccountMessage::VUSR_success)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request SUCCESS", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "vacate_unlocked_slot_success"), Unicode::emptyString);
// let the client know so it can change the character to be properly displayed on the character select screen
Client * const client = getClient();
if (client)
{
GenericValueTypeMessage<std::pair<std::string, NetworkId> > const characterChangedNormal("CharacterChangedNormal", std::make_pair(GameServer::getInstance().getClusterName(), getNetworkId()));
client->send(characterChangedNormal, true);
}
}
else if (response == LoginUpgradeAccountMessage::VUSR_db_error)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request FAILED - internal db error", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "vacate_unlocked_slot_db_error"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::VUSR_account_has_no_unlocked_slot)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request FAILED - account doesn't have an unlocked slot", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "vacate_unlocked_slot_db_no_unlocked_slot"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::VUSR_not_unlocked_slot_character)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request FAILED - character is not an unlocked slot character", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "vacate_unlocked_slot_db_not_unlocked_slot_character"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::VUSR_no_available_normal_character_slot)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request FAILED - no available normal character slot for the account on this galaxy", PlayerObject::getAccountDescription(this).c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "vacate_unlocked_slot_db_no_available_normal_character_slot"), Unicode::emptyString);
}
else
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s VacateUnlockedSlot request FAILED - unknown result code (%d)", PlayerObject::getAccountDescription(this).c_str(), static_cast<int>(response)));
}
}
}
else if (message.getMethod() == "C++SwapUnlockedSlotRsp")
{
if (!message.getPackedDataVector().empty())
{
Archive::ByteStream bs;
bs.put(reinterpret_cast<const void *>(&message.getPackedDataVector()[0]), static_cast<unsigned int>(message.getPackedDataVector().size()));
Archive::ReadIterator ri = bs.begin();
GenericValueTypeMessage<std::pair<std::pair<int, NetworkId>, std::pair<uint32, std::pair<NetworkId, std::string> > > > const swapUnlockedSlotRsp(ri);
LoginUpgradeAccountMessage::SwapUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::SwapUnlockedSlotResponse>(swapUnlockedSlotRsp.getValue().first.first);
if (response == LoginUpgradeAccountMessage::SUSR_success)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s (%s) request SUCCESS", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second.second.c_str()));
ProsePackage prosePackage;
prosePackage.stringId = StringId("unlocked_slot", "swap_unlocked_slot_success");
prosePackage.target.str = Unicode::narrowToWide(swapUnlockedSlotRsp.getValue().second.second.second);
Chat::sendSystemMessage(*this, prosePackage);
// let the client know so it can change the character to be properly displayed on the character select screen
Client * const client = getClient();
if (client)
{
GenericValueTypeMessage<std::pair<std::string, NetworkId> > const characterChangedNormal("CharacterChangedNormal", std::make_pair(GameServer::getInstance().getClusterName(), getNetworkId()));
client->send(characterChangedNormal, true);
GenericValueTypeMessage<std::pair<std::string, NetworkId> > const characterChangedUnlocked("CharacterChangedUnlocked", std::make_pair(GameServer::getInstance().getClusterName(), swapUnlockedSlotRsp.getValue().second.second.first));
client->send(characterChangedUnlocked, true);
}
}
else if (response == LoginUpgradeAccountMessage::SUSR_db_error)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - internal db error", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "swap_unlocked_slot_db_error"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::SUSR_account_has_no_unlocked_slot)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - account doesn't have an unlocked slot", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "swap_unlocked_slot_db_no_unlocked_slot"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::SUSR_not_unlocked_slot_character)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - source character is not an unlocked slot character", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "swap_unlocked_slot_db_not_unlocked_slot_character"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::SUSR_invalid_target_character)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - target character is either not valid, not on the same account, or not on this galaxy", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "swap_unlocked_slot_db_invalid_target_character"), Unicode::emptyString);
}
else if (response == LoginUpgradeAccountMessage::SUSR_target_character_already_unlocked_slot_character)
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - target character is already an unlocked slot character", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str()));
Chat::sendSystemMessage(*this, StringId("unlocked_slot", "swap_unlocked_slot_db_target_is_already_unlocked_character"), Unicode::emptyString);
}
else
{
// CS log the response
LOG("CustomerService",("JediUnlockedSlot:%s SwapUnlockedSlot with %s request FAILED - unknown result code (%d)", PlayerObject::getAccountDescription(this).c_str(), swapUnlockedSlotRsp.getValue().second.second.first.getValueString().c_str(), swapUnlockedSlotRsp.getValue().first.first));
}
}
}
else if (message.getMethod() == "C++AccountFeatureIdResponse")
{
if (!message.getPackedDataVector().empty())
{
Archive::ByteStream bs;
bs.put(reinterpret_cast<const void *>(&message.getPackedDataVector()[0]), static_cast<unsigned int>(message.getPackedDataVector().size()));
Archive::ReadIterator ri = bs.begin();
AccountFeatureIdResponse const msg(ri);
if ((msg.getTarget() == getNetworkId()) && (msg.getResultCode() == RESULT_SUCCESS))
{
Client * client = getClient();
if (client)
{
if (msg.getGameCode() == PlatformGameCode::SWG)
client->setAccountFeatureIds(msg.getFeatureIds());
}
}
if ((msg.getRequestReason() == AccountFeatureIdRequest::RR_ConsoleCommandReloadRequest) && msg.getRequester().isValid() && (msg.getRequester() == getNetworkId()))
{
if (msg.getResultCode() == RESULT_SUCCESS)
{
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("request for feature Id for account (%lu) was successful", msg.getTargetStationId()),
getClient());
if (msg.getGameCode() == PlatformGameCode::SWG)
{
ConsoleMgr::broadcastString("you can use the /getAccountInfo command on a targeted character to view the character's account SWG feature Id",
getClient());
}
if (msg.getResultCameFromSession())
{
std::map<uint32, std::string> const & sessionFeatureIdsData = msg.getSessionFeatureIdsData();
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("session/Platform reported successful result code (%u, %s:%s), (%d) feature Ids for account (%lu)", msg.getResultCode(), msg.getSessionResultString().c_str(), msg.getSessionResultText().c_str(), sessionFeatureIdsData.size(), msg.getTargetStationId()),
getClient());
for (std::map<uint32, std::string>::const_iterator iter = sessionFeatureIdsData.begin(); iter != sessionFeatureIdsData.end(); ++iter)
{
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("%lu (%s)", iter->first, iter->second.c_str()),
getClient());
}
}
else if (msg.getGameCode() != PlatformGameCode::SWG)
{
std::map<uint32, int> const & featureIds = msg.getFeatureIds();
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("(%d) feature Ids for account (%lu)", featureIds.size(), msg.getTargetStationId()),
getClient());
for (std::map<uint32, int>::const_iterator iter = featureIds.begin(); iter != featureIds.end(); ++iter)
{
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("%lu (%d)", iter->first, iter->second),
getClient());
}
}
}
else
{
if (msg.getResultCameFromSession())
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("request for feature Id for account (%lu) failed with error code (%u, %s:%s)", msg.getTargetStationId(), msg.getResultCode(), msg.getSessionResultString().c_str(), msg.getSessionResultText().c_str()), getClient());
else
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("request for feature Id for account (%lu) failed with error code (%u)", msg.getTargetStationId(), msg.getResultCode()), getClient());
}
}
if ((msg.getRequestReason() == AccountFeatureIdRequest::RR_ReloadRewardCheck) && msg.getRequester().isValid() && (msg.getRequester() == getNetworkId()) && (msg.getGameCode() == PlatformGameCode::SWG))
{
// let script know the that account feature id has been
// retrieved so it can resume the rewards claim process
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "veteranAccountFeatureIdResponse", std::vector<int8>(), 0, false);
}
}
}
else if (message.getMethod() == "C++FeatureIdTransactionResponse")
{
if (!message.getPackedDataVector().empty())
{
Archive::ByteStream bs;
bs.put(reinterpret_cast<const void *>(&message.getPackedDataVector()[0]), static_cast<unsigned int>(message.getPackedDataVector().size()));
Archive::ReadIterator ri = bs.begin();
FeatureIdTransactionResponse const msg(ri);
VeteranRewardManager::verifyFeatureIdTransactions(*this, msg.getTransactions());
}
}
else if (message.getMethod() == "C++TransferReplyMoveValidation")
{
if (!message.getPackedDataVector().empty())
{
Archive::ByteStream bs;
bs.put(reinterpret_cast<const void *>(&message.getPackedDataVector()[0]), static_cast<unsigned int>(message.getPackedDataVector().size()));
Archive::ReadIterator ri = bs.begin();
TransferReplyMoveValidation const msg(ri);
// we should only be here if the validation failed
TransferRequestMoveValidation::TransferRequestSource const transferRequestSource = msg.getTransferRequestSource();
if (transferRequestSource == TransferRequestMoveValidation::TRS_console_god_command)
{
TransferReplyMoveValidation::TransferReplyMoveValidationResult const result = msg.getResult();
if (result == TransferReplyMoveValidation::TRMVR_cannot_create_regular_character)
{
ConsoleMgr::broadcastString(FormattedString<256>().sprintf("the destination station id (%u) is not allowed to create any more character on the destination galaxy (%s)", msg.getDestinationStationId(), msg.getDestinationGalaxy().c_str()), getClient());
}
else if (result == TransferReplyMoveValidation::TRMVR_destination_galaxy_invalid)
{
ConsoleMgr::broadcastString(FormattedString<256>().sprintf("either the destination galaxy (%s) is invalid, or the destination galaxy (%s) is not using the same login server as this galaxy", msg.getDestinationGalaxy().c_str(), msg.getDestinationGalaxy().c_str()), getClient());
}
else if (result == TransferReplyMoveValidation::TRMVR_destination_galaxy_not_connected)
{
ConsoleMgr::broadcastString(FormattedString<256>().sprintf("the destination galaxy (%s) is not currently started", msg.getDestinationGalaxy().c_str()), getClient());
}
else if (result == TransferReplyMoveValidation::TRMVR_destination_galaxy_in_loading)
{
ConsoleMgr::broadcastString(FormattedString<256>().sprintf("the destination galaxy (%s) is currently loading", msg.getDestinationGalaxy().c_str()), getClient());
}
else
{
ConsoleMgr::broadcastString(FormattedString<256>().sprintf("transfer validation failed with error code (%d)", static_cast<int>(result)), getClient());
}
}
else if ((transferRequestSource == TransferRequestMoveValidation::TRS_ingame_freects_command_validate) || (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_freects_command_transfer))
{
TransferReplyMoveValidation::TransferReplyMoveValidationResult const result = msg.getResult();
if (result == TransferReplyMoveValidation::TRMVR_cannot_create_regular_character)
{
ScriptParams params;
params.addParam(msg.getDestinationGalaxy().c_str(), "destinationGalaxy");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleFreeCtsValidateFailCannotCreateCharacter", dictionary->getSerializedData(), 0, false);
}
}
else if ((result == TransferReplyMoveValidation::TRMVR_destination_galaxy_invalid) ||
(result == TransferReplyMoveValidation::TRMVR_destination_galaxy_not_connected) ||
(result == TransferReplyMoveValidation::TRMVR_destination_galaxy_in_loading))
{
ScriptParams params;
params.addParam(msg.getDestinationGalaxy().c_str(), "destinationGalaxy");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleFreeCtsValidateFailDestGalaxyUnavailable", dictionary->getSerializedData(), 0, false);
}
}
else
{
LOG("CustomerService", ("CharacterTransfer: transfer validation failed for [%u, %s, %s (%s)] to [%u, %s, %s] with unexpected error code (%d)", msg.getSourceStationId(), msg.getSourceGalaxy().c_str(), msg.getSourceCharacter().c_str(), msg.getSourceCharacterId().getValueString().c_str(), msg.getDestinationStationId(), msg.getDestinationGalaxy().c_str(), msg.getDestinationCharacter().c_str(), static_cast<int>(result)));
}
}
else if ((transferRequestSource == TransferRequestMoveValidation::TRS_ingame_cts_command_validate) || (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_cts_command_transfer))
{
TransferReplyMoveValidation::TransferReplyMoveValidationResult const result = msg.getResult();
if (result == TransferReplyMoveValidation::TRMVR_cannot_create_regular_character)
{
ScriptParams params;
params.addParam(msg.getDestinationGalaxy().c_str(), "destinationGalaxy");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleCtsValidateFailCannotCreateCharacter", dictionary->getSerializedData(), 0, false);
}
}
else if ((result == TransferReplyMoveValidation::TRMVR_destination_galaxy_invalid) ||
(result == TransferReplyMoveValidation::TRMVR_destination_galaxy_not_connected) ||
(result == TransferReplyMoveValidation::TRMVR_destination_galaxy_in_loading))
{
ScriptParams params;
params.addParam(msg.getDestinationGalaxy().c_str(), "destinationGalaxy");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleCtsValidateFailDestGalaxyUnavailable", dictionary->getSerializedData(), 0, false);
}
}
else
{
LOG("CustomerService", ("CharacterTransfer: transfer validation failed for [%u, %s, %s (%s)] to [%u, %s, %s] with unexpected error code (%d)", msg.getSourceStationId(), msg.getSourceGalaxy().c_str(), msg.getSourceCharacter().c_str(), msg.getSourceCharacterId().getValueString().c_str(), msg.getDestinationStationId(), msg.getDestinationGalaxy().c_str(), msg.getDestinationCharacter().c_str(), static_cast<int>(result)));
}
}
}
}
else if (message.getMethod() == "C++TransferReplyNameValidation")
{
if (!message.getPackedDataVector().empty())
{
Archive::ByteStream bs;
bs.put(reinterpret_cast<const void *>(&message.getPackedDataVector()[0]), static_cast<unsigned int>(message.getPackedDataVector().size()));
Archive::ReadIterator ri = bs.begin();
GenericValueTypeMessage<std::pair<std::string, TransferCharacterData> > const msg(ri);
TransferRequestMoveValidation::TransferRequestSource const transferRequestSource = msg.getValue().second.getTransferRequestSource();
if (!msg.getValue().second.getIsValidName())
{
if (transferRequestSource == TransferRequestMoveValidation::TRS_console_god_command)
{
ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("the character name (%s) is not available on galaxy (%s) because of reason (%s)", msg.getValue().second.getDestinationCharacterName().c_str(), msg.getValue().second.getDestinationGalaxy().c_str(), msg.getValue().first.c_str()), getClient());
}
else if ((transferRequestSource == TransferRequestMoveValidation::TRS_ingame_freects_command_validate) || (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_freects_command_transfer))
{
ScriptParams params;
params.addParam(msg.getValue().second.getDestinationGalaxy().c_str(), "destinationGalaxy");
params.addParam(msg.getValue().second.getDestinationCharacterName().c_str(), "destinationCharacterName");
params.addParam(msg.getValue().first.c_str(), "reason");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleFreeCtsValidateFailNameValidation", dictionary->getSerializedData(), 0, false);
}
}
else if ((transferRequestSource == TransferRequestMoveValidation::TRS_ingame_cts_command_validate) || (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_cts_command_transfer))
{
ScriptParams params;
params.addParam(msg.getValue().second.getDestinationGalaxy().c_str(), "destinationGalaxy");
params.addParam(msg.getValue().second.getDestinationCharacterName().c_str(), "destinationCharacterName");
params.addParam(msg.getValue().first.c_str(), "reason");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleCtsValidateFailNameValidation", dictionary->getSerializedData(), 0, false);
}
}
}
else if (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_freects_command_validate)
{
ScriptParams params;
params.addParam(msg.getValue().second.getDestinationGalaxy().c_str(), "destinationGalaxy");
params.addParam(msg.getValue().second.getDestinationCharacterName().c_str(), "destinationCharacterName");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleFreeCtsValidateSuccess", dictionary->getSerializedData(), 0, false);
}
}
else if (transferRequestSource == TransferRequestMoveValidation::TRS_ingame_cts_command_validate)
{
ScriptParams params;
params.addParam(msg.getValue().second.getDestinationGalaxy().c_str(), "destinationGalaxy");
params.addParam(msg.getValue().second.getDestinationCharacterName().c_str(), "destinationCharacterName");
ScriptDictionaryPtr dictionary;
GameScriptObject::makeScriptDictionary(params, dictionary);
if (dictionary.get() != NULL)
{
dictionary->serialize();
MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "handleCtsValidateSuccess", dictionary->getSerializedData(), 0, false);
}
}
}
}
else if (message.getMethod() == "C++PickupAllRoomItemsIntoInventory")
{
// use while(true) loop to use break to discontinue processing and go to the end of loop to run cleanup code
PlayerObject * playerObject = NULL;
while (true)
{
playerObject = PlayerCreatureController::getPlayerObject(this);
if (!playerObject)
break;
// player has disconnected
if (!getClient())
break;
// don't allow this in god mode because it could be used accidentally and pick up a bunch of stuff,
// and god mode allows stuff to be picked up, which wouldn't normally be allowed to be picked up
if (getClient()->isGod())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because you switched into GOD mode."), Unicode::emptyString);
break;
}
// player is not "active", meaning he's probably doing it with a macro
if (playerObject->getSessionLastActiveTime() == 0)
break;
int operationId = 0;
if (!getObjVars().getItem("pickupDropAllItemsOperation.operationId", operationId) || (operationId == 0))
break;
if (operationId != playerObject->getPickupDropAllItemsOperationId())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because of an unexpected condition. Please try the operation again."), Unicode::emptyString);
break;
}
NetworkId const & cellId = playerObject->getPickupDropAllItemsCellId();
ServerObject const * const containingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
if (!containingCell || !containingCell->asCellObject() || (containingCell->getNetworkId() != cellId))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
ServerObject const * const containingPOBso = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*containingCell));
if (!containingPOBso)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
if (!containingPOBso->isInWorldCell())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
TangibleObject const * const containingPOB = containingPOBso->asTangibleObject();
if (!containingPOB)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
if (!containingPOB->isOnAdminList(*this))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because you are no longer the the owner or an admin of the structure or POB ship."), Unicode::emptyString);
break;
}
ServerObject * const targetInventoryObj = getInventory();
if (!targetInventoryObj)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
VolumeContainer * const targetInventoryContainer = ContainerInterface::getVolumeContainer(*targetInventoryObj);
if (!targetInventoryContainer)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
int const inventoryBaseLimit = targetInventoryContainer->getTotalVolume();
int const inventoryLimit = (playerObject->getPickupAllItemsAllowInventoryOverload() ? std::max(inventoryBaseLimit, (inventoryBaseLimit + ConfigServerGame::getMoveValidationMaxInventoryOverload() - 11)) : inventoryBaseLimit);
if (targetInventoryContainer->getCurrentVolume() >= inventoryLimit)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because your inventory is full."), Unicode::emptyString);
// if they didn't run the command with the -overload
// option, remind them about the -overload option
if (!playerObject->getPickupAllItemsAllowInventoryOverload())
Chat::sendSystemMessage(*this, Unicode::narrowToWide("If you wish to pick up more items than available inventory space, you can specify the \"overload\" option with the command by typing \"/pickupAllRoomItemsIntoInventory -overload\" which will pick up additional items and overload your inventory, but not so overloaded as to prevent you from moving."), Unicode::emptyString);
break;
}
Container const * const cellContainer = ContainerInterface::getContainer(*containingCell);
if (!cellContainer)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
std::set<NetworkId> & processedItemsSoFar = playerObject->getPickupDropAllItemsProcessedItemsSoFar();
std::list<CachedNetworkId> cellContents;
{
for (ContainerConstIterator iter = cellContainer->begin(); iter != cellContainer->end(); ++iter)
{
if ((*iter != getNetworkId()) && (processedItemsSoFar.count(*iter) <= 0))
cellContents.push_back(*iter);
}
}
if (cellContents.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because no more items in the room can be picked up or the room is empty."), Unicode::emptyString);
break;
}
// if necessary, temporary increase the inventory limit to allow overload
if (inventoryLimit > inventoryBaseLimit)
targetInventoryContainer->debugDoNotUseSetCapacity(inventoryLimit);
int numberOfItemsPickedUpSoFar = playerObject->getPickupDropAllItemsNumItemsSoFar();
char buffer[512];
Vector const pos = Transform::identity.getPosition_p();
snprintf(buffer, sizeof(buffer)-1, " %s -1 %f %f %f", targetInventoryObj->getNetworkId().getValueString().c_str(), pos.x, pos.y, pos.z);
buffer[sizeof(buffer)-1] = '\0';
Unicode::String const commandTransferItemMiscParams = Unicode::narrowToWide(buffer);
static uint32 const hashTransferItemMisc = Crc::normalizeAndCalculate("transferItemMisc");
Command const & commandTransferItemMisc = CommandTable::getCommand(hashTransferItemMisc);
ServerObject const * cellContent;
bool blockedByNoTrade;
bool showPickUpMenu;
Container::ContainerErrorCode cec;
int previousVolume, newVolume;
int numberOfItemsToProcessThisRound = ConfigServerGame::getPickDropAllRoomItemsNumberOfItemsPerRound();
for (std::list<CachedNetworkId>::const_iterator iter = cellContents.begin(); iter != cellContents.end(); ++iter)
{
--numberOfItemsToProcessThisRound;
processedItemsSoFar.insert(*iter);
cellContent = safe_cast<ServerObject const *>(iter->getObject());
if (!cellContent)
continue;
if (ContainerInterface::getContainedByObject(*cellContent) != containingCell)
continue;
// see if the item would normally have the "Pick Up" menu item on it
if (!PlayerCreatureController::objectMenuRequestCanManipulateObject(*this, *cellContent, blockedByNoTrade, showPickUpMenu))
continue;
if (!showPickUpMenu)
continue;
// see if item would fit in inventory
if (!targetInventoryContainer->mayAdd(*cellContent, cec))
continue;
// "Pick Up" the item, just as if the player had used the "Pick Up" radial or /pickup command
previousVolume = targetInventoryContainer->getCurrentVolume();
CommandCppFuncs::commandFuncTransferMisc(commandTransferItemMisc, getNetworkId(), cellContent->getNetworkId(), commandTransferItemMiscParams);
// stop trying when inventory is full
newVolume = targetInventoryContainer->getCurrentVolume();
if (newVolume >= inventoryLimit)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because your inventory is full."), Unicode::emptyString);
// if they didn't run the command with the -overload
// option, remind them about the -overload option
if (!playerObject->getPickupAllItemsAllowInventoryOverload())
Chat::sendSystemMessage(*this, Unicode::narrowToWide("If you wish to pick up more items than available inventory space, you can specify the \"overload\" option with the command by typing \"/pickupAllRoomItemsIntoInventory -overload\" which will pick up additional items and overload your inventory, but not so overloaded as to prevent you from moving."), Unicode::emptyString);
break;
}
// update count of how many items have been picked up for the operation
numberOfItemsPickedUpSoFar += std::max(0, (newVolume - previousVolume));
// stop trying when enough items have been picked up for the operation
if (numberOfItemsPickedUpSoFar > inventoryLimit)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because \"enough\" items have been picked up."), Unicode::emptyString);
break;
}
// only do so many items each round, to spread out the load
if (numberOfItemsToProcessThisRound <= 0)
break;
}
// if necessary, restore the inventory limit
if (inventoryLimit > inventoryBaseLimit)
targetInventoryContainer->debugDoNotUseSetCapacity(inventoryBaseLimit);
// stop if inventory is full
if (targetInventoryContainer->getCurrentVolume() >= inventoryLimit)
break;
// stop if enough items have been picked up for the operation
if (numberOfItemsPickedUpSoFar > inventoryLimit)
break;
else
playerObject->setPickupDropAllItemsNumItemsSoFar(numberOfItemsPickedUpSoFar);
// stop if no more items can be picked up
if (numberOfItemsToProcessThisRound > 0)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /pickupAllRoomItemsIntoInventory operation has been terminated because no more items in the room can be picked up or the room is empty."), Unicode::emptyString);
break;
}
// queue up the next round of pickup
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++PickupAllRoomItemsIntoInventory",
"",
1,
false);
return;
}
// if we reach this point, it means we are stopping the operation, so cleanup
removeObjVarItem("pickupDropAllItemsOperation");
if (playerObject)
{
playerObject->setPickupDropAllItemsOperationId(0);
playerObject->setPickupDropAllItemsCellId(NetworkId::cms_invalid);
playerObject->setPickupAllItemsAllowInventoryOverload(false);
playerObject->setPickupDropAllItemsNumItemsSoFar(0);
playerObject->getPickupDropAllItemsProcessedItemsSoFar().clear();
}
}
else if (message.getMethod() == "C++DropAllInventoryItemsIntoRoom")
{
// use while(true) loop to use break to discontinue processing and go to the end of loop to run cleanup code
PlayerObject * playerObject = NULL;
while (true)
{
playerObject = PlayerCreatureController::getPlayerObject(this);
if (!playerObject)
break;
// player has disconnected
if (!getClient())
break;
// don't allow this in god mode because it could be used accidentally and drop a bunch of stuff,
// and god mode allows stuff to be dropped, which wouldn't normally be allowed to be dropped
if (getClient()->isGod())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because you switched into GOD mode."), Unicode::emptyString);
break;
}
// player is not "active", meaning he's probably doing it with a macro
if (playerObject->getSessionLastActiveTime() == 0)
break;
int operationId = 0;
if (!getObjVars().getItem("pickupDropAllItemsOperation.operationId", operationId) || (operationId == 0))
break;
if (operationId != playerObject->getPickupDropAllItemsOperationId())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because of an unexpected condition. Please try the operation again."), Unicode::emptyString);
break;
}
NetworkId const & cellId = playerObject->getPickupDropAllItemsCellId();
ServerObject const * const containingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
if (!containingCell || !containingCell->asCellObject() || (containingCell->getNetworkId() != cellId))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
ServerObject const * const containingPOBso = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*containingCell));
if (!containingPOBso)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
if (!containingPOBso->isInWorldCell())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because you have left the room."), Unicode::emptyString);
break;
}
TangibleObject const * const containingPOB = containingPOBso->asTangibleObject();
if (!containingPOB)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
if (!containingPOB->isOnAdminList(*this))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because you are no longer the the owner or an admin of the structure or POB ship."), Unicode::emptyString);
break;
}
int pobBaseItemLimit = 0;
int pobAdditionalItems = 0;
const int pobItemLimit = containingPOB->getPobTotalItemLimit(pobBaseItemLimit, pobAdditionalItems);
if (containingPOB->getPobItemCount() >= pobItemLimit)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because the structure or POB ship is full."), Unicode::emptyString);
break;
}
ServerObject const * const targetInventoryObj = getInventory();
if (!targetInventoryObj)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
VolumeContainer const * const targetInventoryContainer = ContainerInterface::getVolumeContainer(*targetInventoryObj);
if (!targetInventoryContainer)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because of an internal error."), Unicode::emptyString);
break;
}
int const inventoryOverloadLimit = targetInventoryContainer->getTotalVolume() + ConfigServerGame::getMoveValidationMaxInventoryOverload();
std::set<NetworkId> & processedItemsSoFar = playerObject->getPickupDropAllItemsProcessedItemsSoFar();
std::list<CachedNetworkId> inventoryContents;
{
for (ContainerConstIterator iter = targetInventoryContainer->begin(); iter != targetInventoryContainer->end(); ++iter)
{
if (processedItemsSoFar.count(*iter) <= 0)
inventoryContents.push_back(*iter);
}
}
if (inventoryContents.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because no more items in your inventory can be dropped or your inventory is empty."), Unicode::emptyString);
break;
}
int numberOfItemsDroppedSoFar = playerObject->getPickupDropAllItemsNumItemsSoFar();
char buffer[512];
Vector const pos = getTransform_o2c().getPosition_p();
snprintf(buffer, sizeof(buffer)-1, " %s -1 %f %f %f", containingCell->getNetworkId().getValueString().c_str(), pos.x, pos.y, pos.z);
buffer[sizeof(buffer)-1] = '\0';
Unicode::String const commandTransferItemMiscParams = Unicode::narrowToWide(buffer);
static uint32 const hashTransferItemMisc = Crc::normalizeAndCalculate("transferItemMisc");
Command const & commandTransferItemMisc = CommandTable::getCommand(hashTransferItemMisc);
ServerObject const * inventoryContent;
int previousVolume, newVolume;
int numberOfItemsToProcessThisRound = ConfigServerGame::getPickDropAllRoomItemsNumberOfItemsPerRound();
for (std::list<CachedNetworkId>::const_iterator iter = inventoryContents.begin(); iter != inventoryContents.end(); ++iter)
{
--numberOfItemsToProcessThisRound;
processedItemsSoFar.insert(*iter);
inventoryContent = safe_cast<ServerObject const *>(iter->getObject());
if (!inventoryContent)
continue;
if (ContainerInterface::getContainedByObject(*inventoryContent) != targetInventoryObj)
continue;
previousVolume = containingPOB->getPobItemCount();
if ((previousVolume + inventoryContent->getVolume()) > pobItemLimit)
continue;
// "Drop" the item, just as if the player had used the "Drop" radial
CommandCppFuncs::commandFuncTransferMisc(commandTransferItemMisc, getNetworkId(), inventoryContent->getNetworkId(), commandTransferItemMiscParams);
// stop trying when POB is full
newVolume = containingPOB->getPobItemCount();
if (newVolume >= pobItemLimit)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because the structure or POB ship is full."), Unicode::emptyString);
break;
}
// update count of how many items have been dropped for the operation
numberOfItemsDroppedSoFar += std::max(0, (newVolume - previousVolume));
// stop trying when enough items have been dropped for the operation
if ((numberOfItemsDroppedSoFar > inventoryOverloadLimit) && (numberOfItemsDroppedSoFar > ConfigServerGame::getPickDropAllRoomItemsNumberOfItemsPerRound()))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because \"enough\" items have been dropped."), Unicode::emptyString);
break;
}
// only do so many items each round, to spread out the load
if (numberOfItemsToProcessThisRound <= 0)
break;
}
// stop if POB is full
if (containingPOB->getPobItemCount() >= pobItemLimit)
break;
// stop if enough items have been dropped for the operation
if ((numberOfItemsDroppedSoFar > inventoryOverloadLimit) && (numberOfItemsDroppedSoFar > ConfigServerGame::getPickDropAllRoomItemsNumberOfItemsPerRound()))
break;
else
playerObject->setPickupDropAllItemsNumItemsSoFar(numberOfItemsDroppedSoFar);
// stop if no more items can be dropped
if (numberOfItemsToProcessThisRound > 0)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The /dropAllInventoryItemsIntoRoom operation has been terminated because no more items in your inventory can be dropped or your inventory is empty."), Unicode::emptyString);
break;
}
// queue up the next round of drop
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++DropAllInventoryItemsIntoRoom",
"",
1,
false);
return;
}
// if we reach this point, it means we are stopping the operation, so cleanup
removeObjVarItem("pickupDropAllItemsOperation");
if (playerObject)
{
playerObject->setPickupDropAllItemsOperationId(0);
playerObject->setPickupDropAllItemsCellId(NetworkId::cms_invalid);
playerObject->setPickupAllItemsAllowInventoryOverload(false);
playerObject->setPickupDropAllItemsNumItemsSoFar(0);
playerObject->getPickupDropAllItemsProcessedItemsSoFar().clear();
}
}
else if (message.getMethod() == "C++RestoreDecorationLayout")
{
// use while(true) loop to use break to discontinue processing and go to the end of loop to run cleanup code
PlayerObject * playerObject = NULL;
while (true)
{
playerObject = PlayerCreatureController::getPlayerObject(this);
if (!playerObject)
break;
// player has disconnected
if (!getClient())
break;
// player is not "active", meaning he's probably doing it with a macro
if (playerObject->getSessionLastActiveTime() == 0)
break;
int operationId = 0;
if (!getObjVars().getItem("restoreDecorationOperation.operationId", operationId) || (operationId == 0))
break;
if (operationId != playerObject->getRestoreDecorationOperationId())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because of an unexpected condition. Please try the operation again."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
ServerObject * const containingCell = safe_cast<ServerObject *>(ContainerInterface::getContainedByObject(*this));
if (!containingCell || !containingCell->asCellObject())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because you have left the structure or POB ship."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
ServerObject * const containingPOBso = safe_cast<ServerObject *>(ContainerInterface::getContainedByObject(*containingCell));
if (!containingPOBso)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because you have left the structure or POB ship."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
if (!containingPOBso->isInWorldCell())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because you have left the structure or POB ship."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
if (containingPOBso->getNetworkId() != playerObject->getRestoreDecorationPobId())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because you have left the structure or POB ship."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
TangibleObject * const containingPOB = containingPOBso->asTangibleObject();
if (!containingPOB)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because of an internal error."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
if (!containingPOB->isOnAdminList(*this))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been terminated because you are no longer the the owner or an admin of the structure or POB ship."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
int numberOfItemsToProcessThisRound = ConfigServerGame::getPickDropAllRoomItemsNumberOfItemsPerRound();
std::list<std::pair<std::pair<CachedNetworkId, CachedNetworkId>, std::pair<Vector, Quaternion> > > & restoreDecorationLayoutList = playerObject->getRestoreDecorationLayoutList();
while (!restoreDecorationLayoutList.empty() && (numberOfItemsToProcessThisRound > 0))
{
std::pair<std::pair<CachedNetworkId, CachedNetworkId>, std::pair<Vector, Quaternion> > const & front = restoreDecorationLayoutList.front();
restoreItemDecorationLayout(*this, *playerObject, front.first.first, *containingPOB, front.first.second, front.second.first, front.second.second);
playerObject->setRestoreDecorationAttemptedCount(playerObject->getRestoreDecorationAttemptedCount() + 1);
--numberOfItemsToProcessThisRound;
restoreDecorationLayoutList.pop_front();
}
if (restoreDecorationLayoutList.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("The restore decoration layout operation has been completed."), Unicode::emptyString);
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("%d items to be moved/restored, %d items attempted, %d items moved/restored.", playerObject->getRestoreDecorationTotalCount(), playerObject->getRestoreDecorationAttemptedCount(), playerObject->getRestoreDecorationSuccessCount())), Unicode::emptyString);
break;
}
// queue up the next round of restore
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++RestoreDecorationLayout",
"",
1,
false);
return;
}
// if we reach this point, it means we are stopping the operation, so cleanup
removeObjVarItem("restoreDecorationOperation");
if (playerObject)
{
playerObject->setRestoreDecorationOperationId(0);
playerObject->setRestoreDecorationPobId(NetworkId::cms_invalid);
playerObject->setRestoreDecorationTotalCount(0);
playerObject->setRestoreDecorationAttemptedCount(0);
playerObject->setRestoreDecorationSuccessCount(0);
playerObject->getRestoreDecorationLayoutList().clear();
}
}
else if (message.getMethod() == "C++CityAddCitizen")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
const int cityId = atoi(params.c_str());
if (cityId > 0)
CityInterface::enterCityChatRoom(cityId, *this);
}
}
else if (message.getMethod() == "C++CityRemoveCitizen")
{
if (!message.getPackedDataVector().empty())
{
std::string const params(message.getPackedDataVector().begin(), message.getPackedDataVector().end());
const int cityId = atoi(params.c_str());
if (cityId > 0)
CityInterface::leaveCityChatRoom(cityId, *this);
}
}
else
{
TangibleObject::handleCMessageTo(message);
}
}
//-----------------------------------------------------------------------
bool CreatureObject::alwaysSendReliableTransform() const
{
return false;
}
//-----------------------------------------------------------------------
bool CreatureObject::immediateLogoutAllowed()
{
// Notes:
// 1. This should only be called on authoritative objects.
// 2. This call is triggered by a client disconnect that we will only see if a client is
// already associated with this object.
// 3. A client is only associated with an object when the object has finished baselines and
// is authoritative; therefore, we should never hit a case where a player that is logging
// in gets disconnected and causes this function to get called.
// 4. Even though this function is phrased as a query, it has some serious side effects.
// Calling it should be treated as if the caller is saying "Prepare for a disconnect" even
// though script can override and prevent an immediate logout.
DEBUG_FATAL(!isAuthoritative(), ("CreatureObject::immediateLogoutAllowed() should only be called on authoritative objects."));
// if we are in an npc conversation, end it
if (isInNpcConversation())
endNpcConversation();
// if we are mounted, dismount.
if (getState(States::RidingMount))
{
CreatureObject *const mountedCreature = getMountedCreature();
if (mountedCreature)
mountedCreature->detachRider(getNetworkId());
}
virtualOnLogout();
LOG("CustomerService", ("Money:%s logging out with %d money", PlayerObject::getAccountDescription(getNetworkId()).c_str(), getTotalMoney()));
ScriptParams params;
return (getScriptObject()->trigAllScripts(Scripting::TRIG_IMMEDIATE_LOGOUT, params) == SCRIPT_CONTINUE);
}
//-----------------------------------------------------------------------
float CreatureObject::getStepHeight() const
{
return static_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getStepHeight();
}
//----------------------------------------------------------------------
float CreatureObject::getApproachTriggerRange() const
{
return static_cast<const ServerCreatureObjectTemplate *>(getObjectTemplate())->getApproachTriggerRange();
}
//----------------------------------------------------------------------
void CreatureObject::setIsBeast(bool newValue)
{
if(m_isBeast.get() != newValue)
{
m_isBeast.set(newValue);
}
}
//----------------------------------------------------------------------
bool CreatureObject::setForceShowHam(bool show)
{
if(!isAuthoritative())
{
return false;
}
m_forceShowHam.set(show);
return true;
}
//----------------------------------------------------------------------
void CreatureObject::setIsStatic(bool newValue)
{
if(m_isStatic.get() != newValue)
{
m_isStatic = newValue;
if (newValue)
{
TerrainObject* const terrain = TerrainObject::getInstance();
if (terrain && terrain->isReferenceObject(this))
terrain->removeReferenceObject(this);
// don't alter static creatures
if (!isPlayerControlled())
setDefaultAlterTime(AlterResult::cms_keepNoAlter);
}
else
{
potentiallyAddToTerrain();
if (!isPlayerControlled())
setDefaultAlterTime(AlterResult::cms_alterQuickly);
}
}
}
//----------------------------------------------------------------------
bool CreatureObject::potentiallyAddToTerrain()
{
//@todo don't make reference object if any of the following are true:
// check is "asleep" (must add when woken up)
TerrainObject* const terrain = TerrainObject::getInstance ();
if ( isAuthoritative() && !getIsStatic() && !getCreatureController()->getHibernate() && terrain && !terrain->isReferenceObject(this) && isInWorldCell() )
{
terrain->addReferenceObject(this);
return true;
}
return false;
}
//----------------------------------------------------------------------
void CreatureObject::virtualOnAddedToWorldCell()
{
TangibleObject::virtualOnAddedToWorldCell();
potentiallyAddToTerrain();
m_lastRegionNotifyPosition = getPosition_w();
m_lastRegionNotifyPosition.x += 1.0f; // force a check
checkNotifyRegions();
}
//------------------------------------------------------------------------------------------
void CreatureObject::virtualOnRemovedFromWorldCell()
{
TangibleObject::virtualOnRemovedFromWorldCell();
TerrainObject* const terrain = TerrainObject::getInstance();
if (terrain && terrain->isReferenceObject(this))
terrain->removeReferenceObject(this);
}
//------------------------------------------------------------------------------------------
void CreatureObject::virtualOnSetAuthority()
{
TangibleObject::virtualOnSetAuthority();
potentiallyAddToTerrain();
for (int i = 0; i < Attributes::NumberOfAttributes; ++i)
m_regeneration[i] = 0;
if (isPlayerControlled())
{
setNumberOfMissionsWantedInMissionBag(ConfigServerGame::getNumberOfMissionsWantedInMissionBag());
if (isInitialized())
{
Client *client = GameServer::getInstance().getClient(getNetworkId());
if (client)
client->onLoadPlayer(*this);
}
}
AICreatureController * const controller = AICreatureController::asAiCreatureController(getController());
if (controller)
controller->setAuthority(true);
//-- Update the creature's group mission critical object set
GroupMissionCriticalObjectsBuilder::updateGroupMissionCriticalObjects(*this, true);
}
//------------------------------------------------------------------------------------------
void CreatureObject::virtualOnReleaseAuthority()
{
TangibleObject::virtualOnReleaseAuthority();
TerrainObject* const terrain = TerrainObject::getInstance();
if (terrain && terrain->isReferenceObject(this))
terrain->removeReferenceObject(this);
AICreatureController * const controller = AICreatureController::asAiCreatureController(getController());
if(controller)
{
if (getProperty(SlopeEffectProperty::getClassPropertyId()) != NULL)
removeProperty(SlopeEffectProperty::getClassPropertyId());
controller->setAuthority(false);
}
}
//------------------------------------------------------------------------------------------
void CreatureObject::virtualOnLogout()
{
TangibleObject::virtualOnLogout();
PlayerObject * const player = PlayerCreatureController::getPlayerObject(this);
if (player != NULL)
player->virtualOnLogout();
}
//------------------------------------------------------------------------------------------
bool CreatureObject::wantSawAttackTriggers() const
{
if (isDisabled())
return false;
return TangibleObject::wantSawAttackTriggers();
}
//----------------------------------------------------------------------
void CreatureObject::addPackedWearable(std::string const &appearanceData, int arrangementIndex, NetworkId const &networkId, uint32 sharedTemplateCrcValue, const BaselinesMessage * weaponSharedBaselines, const BaselinesMessage * weaponSharedNpBaselines)
{
//-- Check if this network id already exists in the wearable list.
size_t const length = m_wearableData.size();
for (size_t i = 0; i < length; ++i)
{
WearableEntry const &entry = m_wearableData[i];
if (entry.m_networkId == networkId)
{
// Check if this entry exists but in a different configuration.
WARNING((entry.m_arrangement != arrangementIndex), ("attempted to add object id=[%s] in packed wearable list multiple times in different arrangements, existing=[%d],new=[%d].", appearanceData.c_str(), entry.m_arrangement, arrangementIndex));
WARNING((static_cast<uint32>(entry.m_objectTemplate) != sharedTemplateCrcValue), ("attempted to add object id=[%s] in packed wearable list multiple times differing only by shared object template crc value, existing=[%d],new=[%d].", appearanceData.c_str(), entry.m_objectTemplate, static_cast<int>(sharedTemplateCrcValue)));
DEBUG_WARNING((entry.m_appearanceString != appearanceData), ("attempted to add object id=[%s] in packed wearable list multiple times differing only by appearance customization data.", appearanceData.c_str()));
// Done, this entry is already in the wearable data list.
// we own the weaponSharedBaselines and weaponSharedBaselines pointers, so if
// we are not going to add them to the wearables list, we must delete them
if (weaponSharedBaselines)
delete weaponSharedBaselines;
if (weaponSharedNpBaselines)
delete weaponSharedNpBaselines;
return;
}
}
//-- Add the new entry.
m_wearableData.push_back(WearableEntry(appearanceData, arrangementIndex, networkId, sharedTemplateCrcValue, weaponSharedBaselines, weaponSharedNpBaselines));
}
//----------------------------------------------------------------------
void CreatureObject::packWearables()
{
// Pack wearables data
SlottedContainer const * const myContainer = ContainerInterface::getSlottedContainer(*this);
if (myContainer)
{
Object const * inventory = 0;
Object const * datapad = 0;
Object const * appearance = 0;
Container::ContainerErrorCode tmp = Container::CEC_Success;
static SlotId const inventorySlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::inventory);
if (inventorySlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(inventorySlot, tmp);
inventory = itemId.getObject();
}
SlotId datapadSlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::datapad);
if (datapadSlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(datapadSlot, tmp);
datapad = itemId.getObject();
}
SlotId appearanceInvSlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::appearance);
if (appearanceInvSlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(appearanceInvSlot, tmp);
appearance = itemId.getObject();
}
for (ContainerConstIterator iter = myContainer->begin(); iter != myContainer->end(); ++iter)
{
Object const * const o = (*iter).getObject();
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
{
TangibleObject const * const tang = so->asTangibleObject();
if (tang)
{
SlottedContainmentProperty const * const scp = ContainerInterface::getSlottedContainmentProperty(*tang);
if (scp)
{
if (scp->isInAppearanceSlot() || tang == inventory || tang == datapad )
{
ConstCharCrcString clientSharedTemplateNameCrcString = ObjectTemplateList::lookUp(tang->getClientSharedTemplateName());
if (clientSharedTemplateNameCrcString.isEmpty())
WARNING(true, ("Could not find crc for [%s]. Wearable will not be streamed to client", tang->getClientSharedTemplateName()));
else if (so->asWeaponObject() != NULL)
{
addPackedWearable(tang->getAppearanceData(), scp->getCurrentArrangement(), tang->getNetworkId(), clientSharedTemplateNameCrcString.getCrc(),
tang->createSharedBaselinesMessage(), tang->createSharedNpBaselinesMessage());
}
else
addPackedWearable(tang->getAppearanceData(), scp->getCurrentArrangement(), tang->getNetworkId(), clientSharedTemplateNameCrcString.getCrc());
}
}
}
}
}
}
if(appearance)
{
SlottedContainer const * const myAppearanceContainer = ContainerInterface::getSlottedContainer(*appearance);
for(ContainerConstIterator iter = myAppearanceContainer->begin(); iter!= myAppearanceContainer->end(); ++iter)
{
Object const * const o = (*iter).getObject();
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
{
TangibleObject const * const tang = so->asTangibleObject();
if (tang)
{
SlottedContainmentProperty const * const scp = ContainerInterface::getSlottedContainmentProperty(*tang);
if (scp)
{
if (scp->isInAppearanceSlot())
{
ConstCharCrcString clientSharedTemplateNameCrcString = ObjectTemplateList::lookUp(tang->getClientSharedTemplateName());
if (clientSharedTemplateNameCrcString.isEmpty())
WARNING(true, ("Could not find crc for [%s]. Wearable will not be streamed to client", tang->getClientSharedTemplateName()));
else
addPackedAppearanceWearable(tang->getAppearanceData(), scp->getCurrentArrangement(), tang->getNetworkId(), clientSharedTemplateNameCrcString.getCrc());
}
}
}
}
}
}
}
}
}
// ----------------------------------------------------------------------
/**
* Called when we get an ack back that a Jedi slot has been added to our account.
* This function should only be called by PlayerObject::addJediToAccountAck()!
*/
void CreatureObject::addJediToAccountAck()
{
// flag success via an objvar so we can end the timed callback C++AddJediSlot
setObjVarItem(OBJVAR_ADD_JEDI_ACK, 1);
} // CreatureObject::addJediToAccountAck
//----------------------------------------------------------------------
void CreatureObject::setNumberOfMissionsWantedInMissionBag(int n)
{
int i = 0;
ServerObject * const missionBag = getMissionBag();
if(missionBag)
{
Container * const container = ContainerInterface::getContainer(*missionBag);
ContainerIterator iter;
for(iter = container->begin(); iter != container->end(); ++iter)
{
Object * const o = (*iter).getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so && so->asMissionObject())
++i;
}
}
if (n - i > 0)
{
if (m_numberOfMissionsWantedInMissionBag <= 0)
{
// place object on mission object create queue
s_missionQueue.push_back(getNetworkId());
}
m_numberOfMissionsWantedInMissionBag = n - i;
}
else if (i - n > 0)
{
// remove mission objects from the bag
int j = 0;
for (iter = container->begin(), j = 0; iter != container->end(), j < i - n; ++iter, ++j)
{
Object * const o = (*iter).getObject();
if (o)
{
ServerObject * const so = o->asServerObject();
if (so && so->asMissionObject())
so->kill();
}
}
}
}
}
//----------------------------------------------------------------------
void CreatureObject::runMissionCreationQueue()
{
int i = ConfigServerGame::getMissionCreationQueueSize();
while(i > 0 && !s_missionQueue.empty())
{
const NetworkId id = s_missionQueue.front();
s_missionQueue.pop_front();
CreatureObject * creature = safe_cast<CreatureObject *>(NetworkIdManager::getObjectById(id));
if(creature)
{
if(creature->isAuthoritative())
{
if(MissionObject::createMissionObjectInCreatureMissionBag(creature))
creature->m_numberOfMissionsWantedInMissionBag--;
if(creature->m_numberOfMissionsWantedInMissionBag > 0)
s_missionQueue.push_back(id);
}
}
i--;
}
}
//----------------------------------------------------------------------
int CreatureObject::getMaxNumberOfLots() const
{
return HOUSING_MAX_LOTS;
}
//----------------------------------------------------------------------
NetworkId CreatureObject::getHouse() const
{
NetworkId houseId;
// the objvar houseId is per player and is used if it exists
if (getObjVars().getItem(OBJVAR_PLAYER_HOUSE_ID, houseId))
return houseId;
return NetworkId::cms_invalid;
}
//----------------------------------------------------------------------
void CreatureObject::setHouse(const CachedNetworkId & houseId)
{
if (isAuthoritative())
{
// store as an objvar to keep this id per player vs. per account
setObjVarItem(OBJVAR_PLAYER_HOUSE_ID, houseId);
}
else
{
sendControllerMessageToAuthServer(CM_setHouse, new MessageQueueGenericValueType<NetworkId>(houseId));
}
}
// ----------------------------------------------------------------------
void CreatureObject::getAvailableLocomotions()
{
getMovementTable()->getAllLocomotions(m_posture.get(), m_states.get(), m_fastLocomotion, m_slowLocomotion, m_stationaryLocomotion);
}
// ----------------------------------------------------------------------
void CreatureObject::calcPvpableState()
{
setPvpable(true);
}
// ----------------------------------------------------------------------
float CreatureObject::getBaseWalkSpeed() const
{
float speed = m_baseWalkSpeed.get();
if (speed < 0.0f)
return safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_walk);
return speed;
}
// ----------------------------------------------------------------------
void CreatureObject::setBaseWalkSpeed(float speed)
{
if (isAuthoritative())
{
if (m_baseWalkSpeed.get() != speed)
{
m_baseWalkSpeed = speed;
requestMovementInfoUpdate();
}
}
else
{
Controller *controller = getController();
if (controller)
controller->appendMessage(
CM_creatureSetBaseWalkSpeed,
speed,
0,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
// ----------------------------------------------------------------------
float CreatureObject::getBaseRunSpeed() const
{
float speed = m_baseRunSpeed.get();
if (speed < 0.0f)
return safe_cast<const SharedCreatureObjectTemplate *>(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_run);
return speed;
}
// ----------------------------------------------------------------------
int CreatureObject::loadPackedHouses()
{
int numHouses = 0;
ServerObject *datapad = getDatapad();
if (datapad)
{
VolumeContainer const * datapadContainer = ContainerInterface::getVolumeContainer(*datapad);
if (datapadContainer)
{
// iterate over contents.
for(ContainerConstIterator iter(datapadContainer->begin());
iter != datapadContainer->end();
++iter)
{
const CachedNetworkId & objId = (*iter);
const ServerObject * obj = safe_cast<const ServerObject *>(objId.getObject());
if (obj && (obj->getGameObjectType() == SharedObjectTemplate::GOT_data_house_control_device))
{
// if object is a house control device, then go ahead and get the first contained item -
// should be the house itself.
SlottedContainer const *hcdContainer = ContainerInterface::getSlottedContainer(*obj);
if (hcdContainer && hcdContainer->getNumberOfItems() > 0)
{
//load the contents for the house.
for(ContainerConstIterator hcdIter(hcdContainer->begin());
hcdIter != hcdContainer->end();
++hcdIter)
{
Object * const houseId = (*hcdIter).getObject();
BuildingObject * const house = (houseId ? houseId->asServerObject()->asBuildingObject() : NULL);
if (house && !house->getContentsLoaded())
{
LOG("CustomerService", ("CharacterTransfer: starting packed house load (%s) for CTS character %s", house->getNetworkId().getValueString().c_str(), PlayerObject::getAccountDescription(this).c_str()));
numHouses++;
house->loadContents();
setLoadCTSPackedHouses(true);
}
}
}
}
}
}
}
return numHouses;
}
// ----------------------------------------------------------------------
std::vector<unsigned char> CreatureObject::uploadCharacterData(const bool withItems, const bool allowOverride)
{
std::vector<unsigned char> result;
if(getClient())
{
GameScriptObject * scriptObject = getScriptObject();
if(scriptObject)
{
int resultSize = 0;
std::vector<unsigned char> tmp;
tmp.resize(ConfigServerGame::getCtsDefaultScriptDictionarySizeBytes());
ScriptParams p;
p.addParam(tmp);
p.addParam(resultSize);
p.addParam(withItems);
p.addParam(allowOverride);
REPORT_LOG(true, ("Calling OnStartCharacterUpload from CreatureObject.cpp\n"));
IGNORE_RETURN(scriptObject->trigAllScripts(Scripting::TRIG_START_CHARACTER_UPLOAD, p));
resultSize = p.getIntParam(1);
tmp = p.getByteArrayParam(0);
int i;
for(i=0; i < resultSize; ++i)
{
result.push_back(tmp[static_cast<size_t>(i)]);
}
LOG("CustomerService", ("CharacterTransfer: CreatureObject::uploadCharacterData() yields a buffer %d bytes long", result.size()));
}
}
return result;
}
// ----------------------------------------------------------------------
bool CreatureObject::receiveCharacterTransferMessage(const std::vector<unsigned char> & packedCharacterData)
{
bool result = false;
GameScriptObject * scriptObject = getScriptObject();
if(scriptObject)
{
ScriptParams p;
p.addParam(packedCharacterData);
if(scriptObject->trigAllScripts(Scripting::TRIG_DOWNLOAD_CHARACTER, p))
{
result = true;
}
}
if (result)
{
// tell CentralServer to not allow login for this character
// until the character has been persisted and unloaded
GenericValueTypeMessage<std::pair<NetworkId, std::pair<std::string, bool> > > const msg(
"SetSceneForPlayer",
std::make_pair(
getNetworkId(),
std::make_pair(ServerWorld::getSceneId(), true)));
GameServer::getInstance().sendToCentralServer(msg);
// set the idle time so that the character will be disconnected
// in ConfigServerGame::getIdleLogoutTimeAfterCharacterTransferSec()
// so that the character will get unloaded after the next save cycle,
// so that the player can log in the character; this should be much
// faster than the default idle disconnect behavior where the character
// would get disconnected in ConfigServerGame::getIdleLogoutTimeSec()
if (getClient())
getClient()->resetIdleTimeAfterCharacterTransfer();
}
return result;
}
// ----------------------------------------------------------------------
void CreatureObject::receiveCharacterTransferStatusMessage(const std::string & statusMessage)
{
GameScriptObject * scriptObject = getScriptObject();
if(scriptObject)
{
ScriptParams p;
p.addParam(statusMessage.c_str());
scriptObject->trigAllScripts(Scripting::TRIG_RECEIVE_CHARACTER_TRANSFER_STATUS_MESSAGE, p);
}
}
// ----------------------------------------------------------------------
void CreatureObject::setBaseRunSpeed(float speed)
{
if (isAuthoritative())
{
if (m_baseRunSpeed.get() != speed)
{
m_baseRunSpeed = speed;
requestMovementInfoUpdate();
}
}
else
{
Controller *controller = getController();
if (controller)
controller->appendMessage(
CM_creatureSetBaseRunSpeed,
speed,
0,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
// ----------------------------------------------------------------------
/**
* Tests if this creature has a bounty on another creature.
* (Virtual function, overriden in SwgCreatureObject)
*
* @param target the creature to test if we have a bounty on
*
* @return true if we have a bounty on the target, false if not
*/
bool CreatureObject::hasBounty(const CreatureObject & target) const
{
//@todo: this should really be a function of the pvp classes, but we
// need this to be virtual
return false;
} // CreatureObject::hasBounty
// ----------------------------------------------------------------------
/**
* Tests if this creature has a bounty on anything.
* (Virtual function, overriden in SwgCreatureObject, can't be pure virtual
* because ServerCreatureObjectTemplate has a function that instantiates a CreatureObject)
*/
bool CreatureObject::hasBounty() const
{
return false;
}
// ----------------------------------------------------------------------
/**
* Return a list of all the bounty hunters targeting this creature.
*
* Overriden in SwgCreatureObject.
*/
std::vector<NetworkId> const & CreatureObject::getJediBountiesOnMe() const
{
static const std::vector<NetworkId> nothing;
return nothing;
}
// ----------------------------------------------------------------------
bool CreatureObject::hasBountyMissionForTarget(const NetworkId & targetId) const
{
const ServerObject * const datapad = getDatapad();
if (!datapad)
return false;
const Container * const container = ContainerInterface::getContainer(*datapad);
if (!container)
return false;
for (ContainerConstIterator iter = container->begin(); iter != container->end(); ++iter)
{
const ServerObject * const serverMissionObject = safe_cast<const ServerObject *>((*iter).getObject());
if (serverMissionObject)
{
const MissionObject * const missionObject = serverMissionObject->asMissionObject();
if (missionObject)
{
static const unsigned int bounty = CrcLowerString::calculateCrc("bounty");
NetworkId bountyTargetId;
if ((missionObject->getMissionType() == bounty) && (missionObject->getObjVars().getItem("objTarget", bountyTargetId)) && (bountyTargetId == targetId))
return true;
}
}
}
return false;
}
// ----------------------------------------------------------------------
void CreatureObject::setAlternateAppearance(std::string const &sharedObjectTemplateName)
{
//-- Ensure we're authoritative.
if (!isAuthoritative())
{
WARNING(true,
("CreatureObject::setAppearanceFromObjectTemplate(): called on non-authoritative object id=[%s], server id=[%d]",
getNetworkId().getValueString().c_str(),
static_cast<int>(GameServer::getInstance().getProcessId())
));
return;
}
// See if we're trying to turn off our alt appearance
std::string const none("none");
if(sharedObjectTemplateName.find(none) != std::string::npos && sharedObjectTemplateName.size() == none.size())
{
GameScriptObject * const scriptObject = this->getScriptObject();
if(scriptObject)
{
ScriptParams params;
params.addParam(getNetworkId());
if(scriptObject->trigAllScripts(Scripting::TRIG_ON_ABOUT_TO_REVERT_APPEARANCE, params) == SCRIPT_OVERRIDE)
return;
m_alternateAppearanceSharedObjectTemplateName.set(sharedObjectTemplateName);
setState(States::Disguised, false);
params.clear();
params.addParam(getNetworkId());
IGNORE_RETURN(scriptObject->trigAllScripts(Scripting::TRIG_REVERTED_APPEARANCE, params));
}
return;
}
//-- Ensure the serverObjectTemplateName exists.
if (sharedObjectTemplateName.empty() || !TreeFile::exists(sharedObjectTemplateName.c_str()))
{
WARNING(true,
("CreatureObject::setAppearanceFromObjectTemplate(): called on id=[%s], server id=[%d]: invalid serverObjectTemplateName [%s]",
getNetworkId().getValueString().c_str(),
static_cast<int>(GameServer::getInstance().getProcessId()),
sharedObjectTemplateName.c_str()
));
return;
}
ObjectTemplate const *sharedObjectTemplate = ObjectTemplateList::fetch(sharedObjectTemplateName);
SharedCreatureObjectTemplate const *const sharedObjectTemplateAsCreature = dynamic_cast<SharedCreatureObjectTemplate const*>(sharedObjectTemplate);
if (sharedObjectTemplate && sharedObjectTemplateAsCreature)
{
//-- Ensure there is an appearance filename.
if (!sharedObjectTemplateAsCreature->getAppearanceFilename().empty())
{
GameScriptObject * const scriptObject = this->getScriptObject();
if(scriptObject)
{
ScriptParams params;
params.addParam(getNetworkId());
params.addParam(sharedObjectTemplateName.c_str());
if(scriptObject->trigAllScripts(Scripting::TRIG_ON_ABOUT_TO_CHANGE_APPEARANCE, params) == SCRIPT_OVERRIDE)
return;
//-- Set the alternate shared object template name since it is different from the natural shared object template name.
m_alternateAppearanceSharedObjectTemplateName.set(sharedObjectTemplateAsCreature->getAppearanceFilename());
setState(States::Disguised, true);
params.clear();
params.addParam(getNetworkId());
IGNORE_RETURN(scriptObject->trigAllScripts(Scripting::TRIG_CHANGED_APPEARANCE, params));
}
}
// Release resources.
sharedObjectTemplate->releaseReference();
sharedObjectTemplate = 0;
}
}
// ======================================================================
bool operator!=(CreatureMod const &a, CreatureMod const &b)
{
return a.mod != b.mod
|| a.startTime != b.startTime
|| a.maxVal != b.maxVal
|| a.currentVal != b.currentVal;
}
// ======================================================================
namespace Archive
{
void put(Archive::ByteStream &target, CreatureAttitude const &state)
{
target.put(&state.m_behavior, 4);
target.put(&state.m_angerState, 4);
target.put(&state.m_fearState, 4);
for (int i = 0; i < MentalStates::NumberOfMentalStates; ++i)
target.put(&state.m_currentValues[i], 4);
}
void get(Archive::ReadIterator &source, CreatureAttitude &state)
{
source.get(&state.m_behavior, 4);
source.get(&state.m_angerState, 4);
source.get(&state.m_fearState, 4);
for (int i = 0; i < MentalStates::NumberOfMentalStates; ++i)
source.get(&state.m_currentValues[i], 4);
}
//----------------------------------------------------------------------
void put(Archive::ByteStream &target, MonitoredCreatureMovement const &state)
{
target.put(&state.m_lastDistance, sizeof(state.m_lastDistance));
target.put(&state.m_skittishness, sizeof(state.m_skittishness));
target.put(&state.m_curve, sizeof(state.m_curve));
}
void get(Archive::ReadIterator &source, MonitoredCreatureMovement &state)
{
source.get(&state.m_lastDistance, sizeof(state.m_lastDistance));
source.get(&state.m_skittishness, sizeof(state.m_skittishness));
source.get(&state.m_curve, sizeof(state.m_curve));
}
//----------------------------------------------------------------------
void put(Archive::ByteStream &target, CreatureMod const &mod)
{
target.put(&mod.startTime, sizeof(mod.startTime));
target.put(&mod.maxVal, sizeof(mod.maxVal));
target.put(&mod.currentVal, sizeof(mod.currentVal));
put(target, mod.mod);
}
void get(Archive::ReadIterator &source, CreatureMod &mod)
{
source.get(&mod.startTime, sizeof(mod.startTime));
source.get(&mod.maxVal, sizeof(mod.maxVal));
source.get(&mod.currentVal, sizeof(mod.currentVal));
get(source, mod.mod);
}
};
//----------------------------------------------------------------------
float CreatureObject::getWalkSpeed () const
{
float walkSpeed = m_walkSpeed.get();
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
if (m_vehiclePhysicsData)
{
walkSpeed = m_vehiclePhysicsData->m_walkSpeed;
}
}
return walkSpeed * getMovementScale() * getMovementPercent();
}
//----------------------------------------------------------------------
float CreatureObject::getRunSpeed () const
{
float runSpeed = m_runSpeed.get();
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
if (m_vehiclePhysicsData)
{
runSpeed = m_vehiclePhysicsData->m_runSpeed;
}
}
return runSpeed * getMovementScale() * getMovementPercent();
}
//----------------------------------------------------------------------
float CreatureObject::getAccelerationForSpeed (float currentSpeed) const
{
float accel = 0.0f;
if (currentSpeed <= getWalkSpeed ())
accel = safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_walk);
else
accel = safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_run);
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
if (m_vehiclePhysicsData)
{
if (m_vehiclePhysicsData->m_runSpeed)
{
const float speedPercent = std::min (1.0f, currentSpeed / m_vehiclePhysicsData->m_runSpeed);
accel = m_vehiclePhysicsData->m_accelerationMin + (m_vehiclePhysicsData->m_accelerationMax - m_vehiclePhysicsData->m_accelerationMin) * speedPercent;
}
}
}
return accel * getAccelScale() * getAccelPercent();
}
//----------------------------------------------------------------------
float CreatureObject::getTurnRateForSpeed (float currentSpeed) const
{
float turnRate = 0.0f;
if (currentSpeed <= getWalkSpeed ())
turnRate = safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_walk);
else
turnRate = safe_cast<const SharedCreatureObjectTemplate*> (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_run);
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
if (m_vehiclePhysicsData)
{
if (m_vehiclePhysicsData->m_runSpeed)
{
const float speedPercent = std::min (1.0f, currentSpeed / m_vehiclePhysicsData->m_runSpeed);
turnRate = convertRadiansToDegrees (m_vehiclePhysicsData->m_turnRateMin + (m_vehiclePhysicsData->m_turnRateMax - m_vehiclePhysicsData->m_turnRateMin) * speedPercent);
}
}
}
return turnRate;
}
//----------------------------------------------------------------------
float CreatureObject::getSlopeMod () const
{
return 1.0f;
}
//-----------------------------------------------------------------------
float CreatureObject::getAcceleration(SharedCreatureObjectTemplate::MovementTypes movementType) const
{
float walk = getAccelerationForSpeed (getWalkSpeed ());
float run = getAccelerationForSpeed (getRunSpeed ());
if (walk < 0.1f)
walk = 0.1f;
if (walk < run)
{
run = walk;
}
if (movementType == SharedCreatureObjectTemplate::MT_walk)
return walk;
else
return run;
}
//-----------------------------------------------------------------------
float CreatureObject::getTurnRate(SharedCreatureObjectTemplate::MovementTypes movementType) const
{
float walk = getTurnRateForSpeed (getWalkSpeed ());
float run = getTurnRateForSpeed (getRunSpeed ());
if (walk < 30.0f)
walk = 30.0f;
if (walk < run)
{
run = walk * 0.5f;
}
if (movementType == SharedCreatureObjectTemplate::MT_walk)
return walk;
else
return run;
}
//----------------------------------------------------------------------
void CreatureObject::updateVehiclePhysicsData ()
{
if (GameObjectTypes::isTypeOf (getGameObjectType (), SharedObjectTemplate::GOT_vehicle))
{
if (!m_vehiclePhysicsData)
m_vehiclePhysicsData = new VehiclePhysicsData;
const CustomizationData *const cd = fetchCustomizationData();
if (cd)
{
VehicleHoverDynamics::readParamsFromCustomizationData (*cd,
m_vehiclePhysicsData->m_minMoveSpeed,
m_vehiclePhysicsData->m_runSpeed,
m_vehiclePhysicsData->m_turnRateMin,
m_vehiclePhysicsData->m_turnRateMax,
m_vehiclePhysicsData->m_accelerationMin,
m_vehiclePhysicsData->m_accelerationMax);
cd->release ();
m_vehiclePhysicsData->m_walkSpeed = 0.5f * m_vehiclePhysicsData->m_runSpeed;
return;
}
}
if (m_vehiclePhysicsData)
{
delete m_vehiclePhysicsData;
m_vehiclePhysicsData = 0;
}
}
//----------------------------------------------------------------------
void CreatureObject::unequipAllItems()
{
//-- Ensure we're authoritative.
if (!isAuthoritative())
{
WARNING(true,
("unequipAllItems() called on object id=[%s], template=[%s] on server id=[%d] but this object is not authoritative.",
getNetworkId().getValueString().c_str(),
getObjectTemplateName(),
static_cast<int>(GameServer::getInstance().getProcessId())
));
return;
}
//-- Get the equipment container for the creature.
SlottedContainer * const equipmentContainer = ContainerInterface::getSlottedContainer(*this);
if (!equipmentContainer)
{
WARNING(true,
("unequipAllItems() called on object id=[%s], template=[%s] on server id=[%d]: failed to get equipment slotted container, error code=[%d].",
getNetworkId().getValueString().c_str(),
getObjectTemplateName(),
static_cast<int>(GameServer::getInstance().getProcessId())
));
return;
}
//-- Get the inventory container for the creature.
Container::ContainerErrorCode errorCode = Container::CEC_Success;
Container::ContainedItem inventoryContainedItem = equipmentContainer->getObjectInSlot(s_inventorySlotId, errorCode);
Object *const inventoryObjectBase = inventoryContainedItem.getObject();
ServerObject *const inventoryObject = inventoryObjectBase ? inventoryObjectBase->asServerObject() : NULL;
if (!inventoryObject || (errorCode != Container::CEC_Success))
{
WARNING(true,
("unequipAllItems() called on object id=[%s], template=[%s] on server id=[%d]: failed to get inventory object from equipment, error code=[%d].",
getNetworkId().getValueString().c_str(),
getObjectTemplateName(),
static_cast<int>(GameServer::getInstance().getProcessId()),
static_cast<int>(errorCode)
));
return;
}
VolumeContainer *const inventoryContainer = ContainerInterface::getVolumeContainer(*inventoryObject);
if (!inventoryContainer)
{
WARNING(true,
("unequipAllItems() called on object id=[%s], template=[%s] on server id=[%d]: inventory object is NULL.",
getNetworkId().getValueString().c_str(),
getObjectTemplateName(),
static_cast<int>(GameServer::getInstance().getProcessId())
));
return;
}
//-- Collect items to move into inventory. This includes items in all slots that players can manipulate.
typedef std::vector<ServerObject*> ServerObjectVector;
ServerObjectVector itemsToTransfer;
{
ContainerIterator const endIt = equipmentContainer->end();
for (ContainerIterator it = equipmentContainer->begin(); it != endIt; ++it)
{
// Get the equipment item.
Container::ContainedItem containedItem = *it;
Object *const objectBase = containedItem.getObject();
ServerObject *const object = objectBase ? objectBase->asServerObject() : NULL;
if (!object)
{
WARNING(true,
("null object in equipment container for object id=[%s]: equipment item id=[%s].",
getNetworkId().getValueString().c_str(),
containedItem.getValueString().c_str()
));
continue;
}
// Get the slotted containment property so we can check the slot characteristics.
SlottedContainmentProperty const *const property = ContainerInterface::getSlottedContainmentProperty(*object);
if (!property)
{
WARNING(true,
("object in equipment container for object id=[%s], equipment item id=[%s] has no SlottedContainmentProperty.",
getNetworkId().getValueString().c_str(),
containedItem.getValueString().c_str()
));
continue;
}
// Check if any of the current arrangement's slots are not player manipulatable.
if (property->canManipulateArrangement(property->getCurrentArrangement()))
itemsToTransfer.push_back(object);
}
}
//-- Transfer selected equipped items to inventory.
{
ServerObjectVector::iterator const endIt = itemsToTransfer.end();
for (ServerObjectVector::iterator it = itemsToTransfer.begin(); it != endIt; ++it)
{
ServerObject *const object = *it;
if (!object)
continue;
ContainerInterface::transferItemToVolumeContainer(*inventoryObject, *object, NULL, errorCode);
WARNING(errorCode != Container::CEC_Success,
("unequipAllItems(): CreatureObject id=[%s] failed to transfer item id=[%s], template=[%s] from equipment to inventory container, container error code [%d].",
getNetworkId().getValueString().c_str(),
object->getNetworkId().getValueString().c_str(),
object->getObjectTemplateName(),
static_cast<int>(errorCode)
));
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::virtualOnSetClient()
{
TangibleObject::virtualOnSetClient();
handleTutorialTransition();
setNumberOfMissionsWantedInMissionBag(ConfigServerGame::getNumberOfMissionsWantedInMissionBag());
}
//----------------------------------------------------------------------
std::vector<WearableEntry> CreatureObject::getWearableData()
{
return m_wearableData.get();
}
//----------------------------------------------------------------------
void CreatureObject::setWearableData(std::vector<WearableEntry> const & packedWearables)
{
m_wearableData.set(packedWearables);
}
//----------------------------------------------------------------------
void CreatureObject::onContainedItemAppearanceDataModified(TangibleObject const & target, std::string const & oldValue, std::string const & value)
{
TangibleObject::onContainedItemAppearanceDataModified(target, oldValue, value);
if(oldValue != value)
{
std::vector<WearableEntry> wearableData = getWearableData();
for(std::vector<WearableEntry>::iterator i = wearableData.begin(); i != wearableData.end(); ++i)
{
if(target.getNetworkId() == i->m_networkId)
{
i->m_appearanceString = value;
setWearableData(wearableData);
//there will only be one object with the target's networkid, so we can return
return;
}
}
}
}
//----------------------------------------------------------------------
void CreatureObject::updateGroup()
{
GroupObject * group = getGroup();
if(group)
{
const std::vector<std::pair<NetworkId, std::string> > & groupMembers = group->getGroupMembers();
// if there are members that might need an update
if(! groupMembers.empty())
{
// setup the update status message that is sent to the client
if(! m_clientMfdStatusUpdateMessage)
{
// this is the first time an update might need to have been sent
m_clientMfdStatusUpdateMessage = new ClientMfdStatusUpdateMessage(getNetworkId(), ServerWorld::getSceneId(), findPosition_w(), ConfigFile::getKeyFloat("ServerGame", "groupLocationUpdateTimer", 1.0f), ConfigFile::getKeyFloat("ServerGame", "groupLocationUpdateMinimumMoveDistance", 64.0f));
}
else
{
// it's already been constructed, update members
m_clientMfdStatusUpdateMessage->setSceneName(ServerWorld::getSceneId());
m_clientMfdStatusUpdateMessage->setWorldCoordinates(findPosition_w());
}
// if there are still members to update since the last calculation, select
// the first one in the list and send the message
if(! m_clientMfdStatusUpdateMessage->getMembersToUpdate().empty())
{
if(m_clientMfdStatusUpdateMessage->updateMemberUpdateTimer(Clock::frameTime()))
{
// default, assume that the object is observed and no messageTo
// is needed
bool sendMessageTo = false;
while(! sendMessageTo && (!m_clientMfdStatusUpdateMessage->getMembersToUpdate().empty()))
{
static Archive::ByteStream bs;
bs.clear();
NetworkId memberId = m_clientMfdStatusUpdateMessage->getMembersToUpdate().back();
m_clientMfdStatusUpdateMessage->getMembersToUpdate().pop_back();
if(memberId != getNetworkId())
{
// cull bad network id's
if(memberId != NetworkId::cms_invalid)
{
Object * obj = NetworkIdManager::getObjectById(memberId);
if(obj)
{
ServerObject * target = obj->asServerObject();
if(target && target->getClient())
{
// check to see whether the target observing this creature
// if it is, then the client will be updated automatically
if(! ObserveTracker::isObserving(*target->getClient(), *this))
{
// this creature is not being observed by the target player
// if it is authoritative on this server, however, send
// the message directly.
if(target->isAuthoritative())
{
target->getClient()->send(*m_clientMfdStatusUpdateMessage, false);
}
else
{
// the object is valid, but
// not authoritative on this
// server, send a messageTO
sendMessageTo = true;
}
} // else normal game operation will take care of updating the client MFD status window
}
else if(target && !target->getClient() && !target->isAuthoritative())
{
// target is valid, but probably a proxy of a player
// controlled object. Send a message to the remote
// server
sendMessageTo = true;
}
else if(! target)
{
WARNING_STRICT_FATAL(true, ("While updating group locations, group member ID %s refers to an object that is not a server object!", memberId.getValueString().c_str()));
}
}
else
{
// not on this server, send a message
sendMessageTo = true;
}
} // else memberId is an invalid NetworkId
} // else memberId is self
if(sendMessageTo)
{
// the group member is not locally authoritative
// on this server and is not being observed, the only
// way to get data to the group member is by sending
// a message to.
bs.clear();
m_clientMfdStatusUpdateMessage->pack(bs);
// send the messageTo to the target object
MessageToQueue::getInstance().sendMessageToC(memberId, "ClientMfdStatusUpdateMessage", std::string(reinterpret_cast<const char * const>(bs.getBuffer()), bs.getSize()), 0, false);
}
}
}
}
// If the message hasn't changed since the last attempt, don't
// bother sending the update
else if(m_clientMfdStatusUpdateMessage->getHasChanged())
{
// add group members to the mfs status update member id list
m_clientMfdStatusUpdateMessage->getMembersToUpdate().clear();
std::vector<std::pair<NetworkId, std::string> >::const_iterator i;
for(i = groupMembers.begin(); i != groupMembers.end(); ++i)
{
if(i->first != NetworkId::cms_invalid && i->first != getNetworkId())
m_clientMfdStatusUpdateMessage->addMemberToUpdate(i->first);
}
}
}
else
{
// the group is empty, clean up any memory that might have been used before
delete m_clientMfdStatusUpdateMessage;
m_clientMfdStatusUpdateMessage = 0;
}
}
else
{
// the group is empty, clean up any memory that might have been used before
delete m_clientMfdStatusUpdateMessage;
m_clientMfdStatusUpdateMessage = 0;
}
if(m_clientMfdStatusUpdateMessage)
{
m_clientMfdStatusUpdateMessage->setHasChanged(false);
}
}
//------------------------------------------------------------------------------------------
void CreatureObject::handleTutorialTransition()
{
// this is where all the code lives for the special transitions for the tutorial...
// much of the implementation is implemented in NewbieTutorial and is called through here
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
// check if this player is coming from the character creation / selection screen
bool isFromLogin = playerObject->getIsFromLogin();
playerObject->setIsFromLogin(false);
bool startTutorial = !ConfigServerGame::getSkipTutorial();
if (startTutorial)
{
// let the npe phase determine whether or not to do the tutorial
startTutorial = (NewbieTutorial::shouldStartTutorial(this));
}
const std::string currentScene = ServerWorld::getSceneId();
if (NewbieTutorial::isInTutorialArea(this))
{
LOG("npe", ("handleTutorialTransition called for object %s in scene %s at location %f %f %f\n", getNetworkId().getValueString().c_str(), currentScene.c_str(), getPosition_w().x, getPosition_w().y, getPosition_w().z));
// check and see if they are loading into a tutorial area. If so, make sure they only have objects in the FileManifest
if (ConfigServerGame::getStripNonFreeAssetsFromPlayersInTutorial())
{
LOG("npe", ("handleTutorialTransition removing any non-free assets from object %s\n", getNetworkId().getValueString().c_str()));
NewbieTutorial::stripNonFreeAssetsFromPlayerInTutorial(this);
}
}
if (startTutorial && currentScene == NewbieTutorial::getSceneId())
{
// if we are in the tutorial scene, force a teleportFixup here so we can determine if we are in a container
handleTeleportFixup(false);
ServerObject const * container = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
// if they are on the tutorial planet, but not in a valid container, create a tutorial instance for them
if (!container || !container->isAuthoritative() || isFromLogin)
{
// the player should be in the tutorial and is in the right place, go ahead and place them
NewbieTutorial::setupCharacterForTutorial(this);
Vector newLocation = getPosition_w();
if (newLocation == Vector(0,0,0))
{
LOG("npe", ("handleTutorialTransition setup tutorial at the origin for object %s\n", getNetworkId().getValueString().c_str()));
}
ServerObject *tutorial = NewbieTutorial::createTutorial(newLocation);
FATAL(!tutorial, ("failed to create tutorial for player which already exists."));
std::string startCellName = NewbieTutorial::getStartCellName();
Vector startCoords = NewbieTutorial::getStartCoords();
LOG("npe", ("handleTutorialTransition created newbie tutorial %s (%s authoritative) for %s (%s authoritative)\n", tutorial->getNetworkId().getValueString().c_str(), (tutorial->isAuthoritative()) ? "is" : "not", getNetworkId().getValueString().c_str(), (isAuthoritative()) ? "is" : "not"));
teleportObject(newLocation, tutorial->getNetworkId(), startCellName, startCoords, "", true);
}
}
else if (
(startTutorial && isFromLogin)
)
{
// if the creature object is coming from login, and they should start the tutorial
// or if the player is on a free trial account, but is on a non-free trial planet or location (NOT GOOD!)
// warp them to the tutorial planet
LOG("npe", ("Warping player (%s) back to tutorial because they are coming from login (%i) or because they are on a free trial account (%i) and trying to get to a non-free trial planet (%s: %f %f %f)\n", getNetworkId().getValueString().c_str(), isFromLogin, (getClient() && getClient()->isFreeTrialAccount()), ServerWorld::getSceneId().c_str(), getPosition_w().x, getPosition_w().y, getPosition_w().z));
NewbieTutorial::setupCharacterForTutorial(this);
GameServer::getInstance().requestSceneWarp(CachedNetworkId(*this), NewbieTutorial::getSceneId(), NewbieTutorial::getTutorialLocation(), NetworkId::cms_invalid, Vector(0,0,0));
}
}
}
//------------------------------------------------------------------------------------------
void CreatureObject::onContainerChildLostItem(ServerObject * destination, ServerObject& item, ServerObject* source, ServerObject* transferer)
{
ServerObject::onContainerChildLostItem(destination, item, source, transferer);
GameScriptObject * gameScriptObject = getScriptObject();
if(gameScriptObject)
{
NetworkId itemId = item.getNetworkId();
NetworkId sourceId;
if(source)
sourceId = source->getNetworkId();
NetworkId transfererId;
if(transferer)
transfererId = transferer->getNetworkId();
NetworkId destinationId;
if(destination)
destinationId = destination->getNetworkId();
ScriptParams params;
params.addParam (itemId);
params.addParam (sourceId);
params.addParam (transfererId);
params.addParam (destinationId);
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_CONTAINER_CHILD_LOST_ITEM, params));
}
if(isPlayerControlled()) // Source == Appearance Inventory
{
//Update wearble appearance data
SlottedContainmentProperty* scp = ContainerInterface::getSlottedContainmentProperty(item);
if (scp)
{
WearableEntry e("", scp->getCurrentArrangement(), item.getNetworkId(), 0);
int index = m_wearableAppearanceData.find(e);
if (index >= 0)
{
m_wearableAppearanceData.erase(index);
}
}
}
}
//------------------------------------------------------------------------------------------
void CreatureObject::onContainerChildGainItem(ServerObject& item, ServerObject* source, ServerObject* transferer)
{
ServerObject::onContainerChildGainItem(item, source, transferer);
GameScriptObject * gameScriptObject = getScriptObject();
if(gameScriptObject)
{
NetworkId itemId = item.getNetworkId();
NetworkId sourceId;
if(source)
sourceId = source->getNetworkId();
NetworkId transfererId;
if(transferer)
transfererId = transferer->getNetworkId();
ScriptParams params;
params.addParam (itemId);
params.addParam (sourceId);
params.addParam (transfererId);
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_CONTAINER_CHILD_GAIN_ITEM, params));
}
if(isPlayerControlled())
{
TangibleObject* tangibleObject = item.asTangibleObject();
if(!tangibleObject)
return;
//Update wearables data
SlottedContainmentProperty* scp = ContainerInterface::getSlottedContainmentProperty(item);
if (scp)
{
Object const * appearance = 0;
SlottedContainer const * const myContainer = ContainerInterface::getSlottedContainer(*this);
if (myContainer)
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
SlotId appearanceInvSlot = SlotIdManager::findSlotId(CreatureObjectNamespace::SlotNames::appearance);
if (appearanceInvSlot != SlotId::invalid)
{
Container::ContainedItem itemId = myContainer->getObjectInSlot(appearanceInvSlot, tmp);
appearance = itemId.getObject();
}
}
Object const * container = ContainerInterface::getContainedByObject(*tangibleObject);
bool const inAppearanceInv = (container == appearance);
if (scp->getCurrentArrangement() >= 0 && scp->isInAppearanceSlot() && inAppearanceInv)
{
ConstCharCrcString clientSharedTemplateNameCrcString = ObjectTemplateList::lookUp(tangibleObject->getClientSharedTemplateName());
if (clientSharedTemplateNameCrcString.isEmpty())
WARNING(true, ("Could not find crc for %s. Wearable will not be streamed to client", tangibleObject->getClientSharedTemplateName()));
else
addPackedAppearanceWearable(tangibleObject->getAppearanceData(), scp->getCurrentArrangement(), tangibleObject->getNetworkId(), clientSharedTemplateNameCrcString.getCrc());
}
}
}
}
//------------------------------------------------------------------------------------------
void CreatureObject::forceNotifyRegionsCheck()
{
m_lastRegionNotifyPosition.x += 1.0f; // force a check
}
//------------------------------------------------------------------------------------------
void CreatureObject::checkNotifyRegions()
{
// if the object is player controlled, check for a change of regions that have their
// notify field set to true.
static const bool allowChecks = ConfigServerGame::getCheckNotifyRegions();
if(! allowChecks)
return;
if(! isPlayerControlled() || !isAuthoritative())
return;
if (! isInWorld())
return;
bool forceCheckRegions = m_regionFlagTimer.isExpired();
if(m_lastRegionNotifyPosition == getPosition_w() && !forceCheckRegions)
return;
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if(! playerObject)
return;
PROFILER_AUTO_BLOCK_DEFINE("CreatureObject::checkNotifyRegions()");
// region containers populated by region master getRegionsAtPoint()
static std::vector<const Region *> newRegions;
newRegions.clear();
RegionMaster::getRegionsAtPoint(ServerWorld::getSceneId(), getPosition_w().x, getPosition_w().z, newRegions);
if(forceCheckRegions)
{
m_regionFlagTimer.reset();
std::vector<const Region *>::iterator regionIter = newRegions.begin();
EnvironmentInfo flags = static_cast<EnvironmentInfo>(0);
for(; regionIter != newRegions.end(); ++regionIter)
{
if((*regionIter)->getNotify())
flags = flags | (*regionIter)->getEnvironmentFlags();
}
playerObject->setEnvironmentFlags(flags);
if(m_lastRegionNotifyPosition == getPosition_w())
return;
}
static std::vector<std::string> newNotifyRegions;
newNotifyRegions.clear();
static std::vector<std::string> newNotifyRegionsEntered;
newNotifyRegionsEntered.clear();
static std::vector<std::pair<std::string, std::string> > notifyRegionsExited;
notifyRegionsExited.clear();
// look for region enter events
std::vector<const Region *>::const_iterator i;
bool teleport = false;
static const bool allowOverride = ConfigServerGame::getAllowRegionTriggerOverride();
// iterate through the new regions looking for a "notify" region that is not in the
// list of regions found during the last check.
for(i = newRegions.begin(); i != newRegions.end(); ++i)
{
if((*i)->getNotify())
{
// this is a region that requires a notification be sent
// to scripts on the player's creature object
std::string regionName = Unicode::wideToNarrow((*i)->getName());
newNotifyRegions.push_back(regionName);
// check the notify regions from the last pass
// to see if this is a new region
if (!m_notifyRegions.contains(std::make_pair(ServerWorld::getSceneId(), regionName)))
{
// the region was not in the list, this is a new region
// and TRIG_ENTER_REGION should be generated
ScriptParams params;
params.addParam(ServerWorld::getSceneId().c_str());
params.addParam(regionName.c_str());
if(getScriptObject())
{
// allow scripts to prevent entry into the region if they
// return SCRIPT_OVERRIDE
if (getScriptObject()->trigAllScripts(Scripting::TRIG_ENTER_REGION, params) == SCRIPT_OVERRIDE)
{
// don't disallow admins, don't disallow if the server
// config has it disabled
if((!getClient() || !getClient()->isGod()) && allowOverride)
{
teleport = true;
Vector teleportTarget = getPosition_w();
Vector regionCenter = (*i)->getCenter();
// if, somehow, a player starts in a forbidden region, allow them to walk out
if(teleportTarget.magnitudeXZBetweenSquared(regionCenter) < m_lastRegionNotifyPosition.magnitudeXZBetweenSquared(regionCenter))
{
// the current position is further into the region,
// push the player back
teleportObject(m_lastRegionNotifyPosition, NetworkId::cms_invalid, "", Vector(), "");
}
else
{
// the current position is closer to the region edge,
// allow the player to move normally in this direction
m_lastRegionNotifyPosition = teleportTarget;
}
// if the player is on a mount/vehicle but is
// not the driver of the mount/vehicle, then
// eject the (passenger) player so he doesn't
// keep trying to enter the region because the
// mount/vehicle will keep taking him into the
// region as long as he remains mounted
CreatureObject const * const mountedCreature = getMountedCreature();
if (mountedCreature && (this != mountedCreature->getPrimaryMountingRider()))
{
static uint32 const commandHash = Crc::normalizeAndCalculate("dismount");
const Command &command = CommandTable::getCommand(static_cast<uint32>(commandHash));
if (!command.isNull())
{
commandQueueEnqueue(command, mountedCreature->getNetworkId(), mountedCreature->getObjectName(), 0, false, static_cast<Command::Priority>(Command::CP_Front), true);
}
}
}
}
}
// if a teleport didn't happen,
// keep this region on the list
// of notify regions the client
// just entered
if(! teleport)
newNotifyRegionsEntered.push_back(regionName);
}
}
}
// look for for region exit events. If a region from the last pass
// is not in the new region list, the player has left the region
Archive::AutoDeltaSet<std::pair<std::string, std::string> >::const_iterator i2;
for(i2 = m_notifyRegions.begin(); i2 != m_notifyRegions.end(); ++i2)
{
if((i2->first != ServerWorld::getSceneId()) || (std::find(newNotifyRegions.begin(), newNotifyRegions.end(), i2->second) == newNotifyRegions.end()))
{
// the player is exiting the region, send a TRIG_EXIT_REGION
// to all scripts attached to the player's creature object
ScriptParams params;
params.addParam(i2->first.c_str());
params.addParam(i2->second.c_str());
if(getScriptObject())
{
if(getScriptObject()->trigAllScripts(Scripting::TRIG_EXIT_REGION, params) == SCRIPT_OVERRIDE)
{
// don't override movement of god characters, or if the
// server config disables move overrides
if((!getClient() || !getClient()->isGod()) && allowOverride && (i2->first == ServerWorld::getSceneId()))
{
teleport = true;
Vector teleportTarget = getPosition_w();
// if the current position is outside the region, fix the
// teleport target to prevent the player from exiting
Region * r = RegionMaster::getRegionByName(i2->first, Unicode::narrowToWide(i2->second));
if (r)
{
if(! r->getBounds().isPointIn(teleportTarget.x, teleportTarget.z))
{
// find a point along the path that is in the retion
Vector heading = r->getCenter() - teleportTarget;
heading.normalize();
int tries = 0;
while(! r->getBounds().isPointIn(teleportTarget.x, teleportTarget.z) && tries < 300)
{
++tries;
teleportTarget += heading;
}
teleportObject(teleportTarget, NetworkId::cms_invalid, "", Vector(), "");
}
}
m_lastRegionNotifyPosition = teleportTarget;
}
}
else
{
notifyRegionsExited.push_back(std::make_pair(i2->first, i2->second));
}
}
}
}
if(!teleport)
{
m_lastRegionNotifyPosition = getPosition_w();
}
std::vector<std::pair<std::string, std::string> >::const_iterator i3;
for(i3 = notifyRegionsExited.begin(); i3 != notifyRegionsExited.end(); ++i3)
{
m_notifyRegions.erase(*i3);
}
std::vector<std::string>::const_iterator i4;
for(i4 = newNotifyRegionsEntered.begin(); i4 != newNotifyRegionsEntered.end(); ++i4)
{
m_notifyRegions.insert(std::make_pair(ServerWorld::getSceneId(), *i4));
}
}
// ----------------------------------------------------------------------
bool CreatureObject::hasCertificationsForItem(const TangibleObject & item) const
{
std::vector<std::string> requiredCerts;
item.getRequiredCertifications(requiredCerts);
for (std::vector<std::string>::const_iterator i=requiredCerts.begin(); i!=requiredCerts.end(); ++i)
{
if (!hasCommand(*i))
return false;
}
return true;
}
//-----------------------------------------------------------------------
CreatureObject * CreatureObject::getCreatureObject(NetworkId const & networkId)
{
return asCreatureObject(NetworkIdManager::getObjectById(networkId));
}
// ----------------------------------------------------------------------
CreatureObject const * CreatureObject::asCreatureObject(Object const * object)
{
ServerObject const * const serverObject = (object != NULL) ? object->asServerObject() : NULL;
return (serverObject != NULL) ? serverObject->asCreatureObject() : NULL;
}
// ----------------------------------------------------------------------
CreatureObject * CreatureObject::asCreatureObject(Object * object)
{
ServerObject * serverObject = (object != NULL) ? object->asServerObject() : NULL;
return (serverObject != NULL) ? serverObject->asCreatureObject() : NULL;
}
// ----------------------------------------------------------------------
void CreatureObject::addMissionCriticalObject(NetworkId const & networkId)
{
if (isAuthoritative())
{
if (m_missionCriticalObjectSet.find(networkId) == m_missionCriticalObjectSet.end())
{
m_missionCriticalObjectSet.insert(networkId);
GroupMissionCriticalObjectsBuilder::updateGroupMissionCriticalObjects(*this, true);
ServerObject * const serverObject = safe_cast<ServerObject *>(NetworkIdManager::getObjectById(networkId));
if (serverObject)
ObserveTracker::onMissionCriticalObjectAdded(*this, *serverObject);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::removeMissionCriticalObject(NetworkId const & networkId)
{
if (isAuthoritative())
{
MissionCriticalObjectSet::iterator iter = m_missionCriticalObjectSet.find(networkId);
if (iter != m_missionCriticalObjectSet.end())
{
m_missionCriticalObjectSet.erase(iter);
GroupMissionCriticalObjectsBuilder::updateGroupMissionCriticalObjects(*this, true);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::clearMissionCriticalObjects()
{
if (isAuthoritative())
{
m_missionCriticalObjectSet.clear();
GroupMissionCriticalObjectsBuilder::updateGroupMissionCriticalObjects(*this, true);
}
}
// ----------------------------------------------------------------------
bool CreatureObject::isMissionCriticalObject(NetworkId const & networkId) const
{
if (isAuthoritative())
return m_missionCriticalObjectSet.find(networkId) != m_missionCriticalObjectSet.end();
return false;
}
// ----------------------------------------------------------------------
CreatureObject::MissionCriticalObjectSet const & CreatureObject::getMissionCriticalObjects() const
{
return m_missionCriticalObjectSet.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setGroupMissionCriticalObjects(GroupMissionCriticalObjectSet const & groupMissionCriticalObjectSet)
{
DEBUG_FATAL(!isAuthoritative(), ("CreatureObject::setGroupMissionCriticalObjects: called on non-authoritative object %s", getDebugInformation(true).c_str()));
if (isAuthoritative())
{
//-- Anything that is not in our set, we need to add
{
for (GroupMissionCriticalObjectSet::const_iterator iter = groupMissionCriticalObjectSet.begin(); iter != groupMissionCriticalObjectSet.end(); ++iter)
if (!m_groupMissionCriticalObjectSet.contains(*iter))
m_groupMissionCriticalObjectSet.insert(*iter);
}
//-- Anything that is not in the original set, we need to remove
{
for (GroupMissionCriticalObjectSet::iterator iter = m_groupMissionCriticalObjectSet.begin(); iter != m_groupMissionCriticalObjectSet.end(); ++iter)
if (groupMissionCriticalObjectSet.find(*iter) == groupMissionCriticalObjectSet.end())
m_groupMissionCriticalObjectSet.erase(*iter);
}
}
}
// ----------------------------------------------------------------------
bool CreatureObject::getCoverVisibility() const
{
return m_coverVisibility.get();
}
// ----------------------------------------------------------------------
void CreatureObject::setCoverVisibility(bool isVisible)
{
if (isAuthoritative())
{
m_coverVisibility.set(isVisible);
}
else
{
sendControllerMessageToAuthServer(CM_setCoverVisibility, new MessageQueueGenericValueType<bool>(isVisible));
}
}
// ----------------------------------------------------------------------
/**
* Called when an attack pushes us away from the attacker. This gets called
* due to a message from the client (which sucks, btw).
*
* @param attackerId the attacker
* @param attackerPos where the client thought the attacker was
* @param defenderPos where the client thought the defender (us) was
* @param distance the distance we are pushed away from the attacker
*/
void CreatureObject::pushedMe(const NetworkId & attackerId,
const Vector & attackerPos, const Vector & defenderPos, float distance)
{
if (isAuthoritative())
{
const ServerObject * attacker = safe_cast<const ServerObject *>(NetworkIdManager::getObjectById(attackerId));
//@todo: add a whole bunch of security checks here
// for now, push us away from the defender, using the server positions
// do a bare minimum sanity check
static const float MAX_DISTANCE = 20.0f;
if (distance > MAX_DISTANCE)
{
WARNING(true, ("CreatureObject::pushedMe told defender %s was pushed by %s %f meters (max = %f)",
getNetworkId().getValueString().c_str(), attackerId.getValueString().c_str(), distance, MAX_DISTANCE));
return;
}
UNREF(defenderPos);
Vector offset(getPosition_w());
if (attacker != NULL)
offset -= attacker->getPosition_w();
else
offset -= attackerPos;
offset.normalize();
offset *= distance;
// this is a hack due to the controller move command needing to know the destination cell
// @todo: change creature movement to always use the CellProperty notification system for moving across cell boundaries
const Vector oldPos(getPosition_p());
// note: unlike the client, this call will not cause us to change cells
move_p(rotate_w2p(offset));
AICreatureController *controller = dynamic_cast<AICreatureController *>(getController());
if (controller != NULL)
{
// we only need to do this for npcs, because we'll use the update from a player's client for players
// we need to do this for npcs to prevent the ai from moving them back to their previous position
const Vector newPos(getPosition_p());
float closestPortalT = 0.0f;
const CellProperty * destinationCell = getParentCell()->getDestinationCell(oldPos, newPos, closestPortalT);
if (destinationCell == NULL || destinationCell == getParentCell())
{
// no cell change
controller->warpTo(getParentCell(), newPos);
}
else
{
Vector warpPos(destinationCell->getOwner().rotateTranslate_w2p(rotateTranslate_p2w(newPos)));
controller->warpTo(destinationCell, warpPos);
}
}
}
else
{
sendControllerMessageToAuthServer(CM_pushCreature,
new MessageQueuePushCreature(attackerId, getNetworkId(), attackerPos, defenderPos, distance));
}
}
// ----------------------------------------------------------------------
/**
* Creates a "slow down" effect between this creature and a defender. The effect acts as if
* there was a hill between the combatants, with the attacker at the top and the defender
* at the bottom. The area of the effect is a cone between the attacker and defender.
* The "hill" will be maintained between the combatants even if they move.
*
* @param defender the target that defines the direction of the "hill"
* @param coneLength the length of the effect cone
* @param coneAngle the angle of the effect cone, in radians
* @param slopeAngle the angle of the "hill", in radians
* @param expireTime the game time when the effect expires
*/
void CreatureObject::addSlowDownEffect(const TangibleObject & defender, float coneLength, float coneAngle, float slopeAngle, unsigned long expireTime)
{
if (isAuthoritative())
{
if (addSlowDownEffectProxy(defender, coneLength, coneAngle, slopeAngle, expireTime))
{
// tell everyone who can see us that we are doing the effect
Controller * const controller = getController();
if (controller)
{
controller->appendMessage(CM_slowDownEffect,
0,
new MessageQueueSlowDownEffect(defender.getNetworkId(), coneLength, coneAngle, slopeAngle, expireTime),
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_PROXY_SERVER |
GameControllerMessageFlags::DEST_ALL_CLIENT);
}
}
}
else
{
sendControllerMessageToAuthServer(CM_slowDownEffect,
new MessageQueueSlowDownEffect(defender.getNetworkId(), coneLength, coneAngle, slopeAngle, expireTime));
}
}
// ----------------------------------------------------------------------
/**
* Creates a "slow down" effect between this creature and a defender. The effect acts as if
* there was a hill between the combatants, with the attacker at the top and the defender
* at the bottom. The area of the effect is a cone between the attacker and defender.
* The "hill" will be maintained between the combatants even if they move.
*
* @param defender the target that defines the direction of the "hill"
* @param coneLength the length of the effect cone
* @param coneAngle the angle of the effect cone, in radians
* @param slopeAngle the angle of the "hill", in radians
* @param expireTime the game time when the effect expires
*
* @return true if the effect was added, false if the creature already had a slow down effect
*/
bool CreatureObject::addSlowDownEffectProxy(const TangibleObject & defender, float coneLength, float coneAngle, float slopeAngle, unsigned long expireTime)
{
// if we already are doing a slowdown, don't do another
Property * property = getProperty(SlowDownProperty::getClassPropertyId());
if (property != NULL)
return false;
property = new SlowDownProperty(*this, CachedNetworkId(defender), coneLength, coneAngle, slopeAngle, expireTime);
addProperty(*property, true);
return true;
}
// ----------------------------------------------------------------------
/**
* Causes any slow down effect being used by us to be canceled early.
*/
void CreatureObject::removeSlowDownEffect()
{
if (isAuthoritative())
{
removeSlowDownEffectProxy();
// tell all my proxies (client and server) to remove the effect
Controller * const controller = getController();
if (controller)
controller->appendMessage(CM_removeSlowDownEffectProxy, 0, NULL, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_ALL_CLIENT | GameControllerMessageFlags::DEST_PROXY_SERVER);
}
else
{
sendControllerMessageToAuthServer(CM_removeSlowDownEffect, NULL);
}
}
// ----------------------------------------------------------------------
/**
* Causes any slow down effect being used by us to be canceled early.
*/
void CreatureObject::removeSlowDownEffectProxy()
{
if (getProperty(SlowDownProperty::getClassPropertyId()) != NULL)
removeProperty(SlowDownProperty::getClassPropertyId());
}
// ----------------------------------------------------------------------
/**
* Adds an additional terrain slope effect that will be taken into account the
* next time we alter this creature. Note that this function only affects non-player
* creatures.
*
* @param normal the normal vector of the "terrain" that is affecting us
*/
void CreatureObject::addTerrainSlopeEffect(const Vector & normal)
{
if (isAuthoritative() && !isPlayerControlled())
{
Property * property = getProperty(SlopeEffectProperty::getClassPropertyId());
if (property == NULL)
{
property = new SlopeEffectProperty(*this);
addProperty(*property, true);
}
safe_cast<SlopeEffectProperty*>(property)->addNormal(normal);
}
}
// ----------------------------------------------------------------------
char const * CreatureObject::getMentalStateString(MentalStates::Enumerator const mentalState)
{
switch (mentalState)
{
case MentalStates::Fear: { return "FEAR"; }
case MentalStates::Anger: { return "ANGER"; }
case MentalStates::Interest: { return "INTEREST"; }
case MentalStates::Distress: { return "DISTRESS"; }
default: {}
}
return "ERROR";
}
// ----------------------------------------------------------------------
char const * CreatureObject::getBehaviorString(Behaviors::Enumerator const behavior)
{
switch (behavior)
{
case Behaviors::Calm: { return "CALM"; }
case Behaviors::Alert: { return "ALERT"; }
case Behaviors::Threaten: { return "THREATEN"; }
case Behaviors::Retreat: { return "RETREAT"; }
case Behaviors::Flee: { return "FLEE"; }
case Behaviors::Attack: { return "ATTACK"; }
case Behaviors::Frenzy: { return "FRENZY"; }
default: {}
}
return "ERROR";
}
// ----------------------------------------------------------------------
void CreatureObject::alterPseudoPlayedTime(float time)
{
if (isAuthoritative())
{
m_pseudoPlayedTime += time;
}
else
{
WARNING(true, ("CreatureObject::alterPlayedTime called on non-authoritative creature"));
}
} // updatePlayedTime
// ----------------------------------------------------------------------
void CreatureObject::setPseudoPlayedTime(float playedTime)
{
m_pseudoPlayedTime = playedTime;
}
// ----------------------------------------------------------------------
void CreatureObject::addBuff(uint32 buffNameCrc, float duration, float dynamicValue, NetworkId caster, uint32 stackCount)
{
addBuff( buffNameCrc, duration, dynamicValue, duration, caster, stackCount);
}
void CreatureObject::addBuff(uint32 buffNameCrc, float timeTillRemoval, float dynamicValue, float totalDuration, NetworkId caster, uint32 stackCount)
{
if (!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_addBuff, new MessageQueueGenericValueType<std::pair<int, std::pair<float, std::pair<float, std::pair< float, NetworkId > > > > >(std::make_pair(static_cast<int>(buffNameCrc), std::make_pair(timeTillRemoval, std::make_pair(dynamicValue, std::make_pair(totalDuration, caster))))));
return;
}
uint32 timestamp = internalGetPlayedTime();
if (BuffManager::getBuffIsCelestial(buffNameCrc))
{
timestamp = ServerClock::getInstance().getGameTimeSeconds();
}
int newStackCount = stackCount;
if(timeTillRemoval < 0.0f)
timestamp = 0xFFFFFFFF;
else if (timeTillRemoval == 0.0f)
{
float defaultDuration = BuffManager::getBuffDefaultDuration(buffNameCrc);
if(defaultDuration < 0.0f)
timestamp = 0xFFFFFFFF;
else
{
timestamp += static_cast<uint32>(defaultDuration);
totalDuration = defaultDuration;
}
}
else
{
timestamp += static_cast<uint32>(timeTillRemoval);
}
bool reapply = false;
if(hasBuff(buffNameCrc))
{
Buff b;
getBuff(buffNameCrc, b);
newStackCount += b.m_stackCount;
if((timestamp != 0xFFFFFFFF) && (b.m_timestamp > timestamp))
{
//We don't need to do anything; the buff we're adding has less time than the one we have
return;
}
std::map<uint32, Buff::PackedBuff>::const_iterator i = m_buffs.find(buffNameCrc);
if (i != m_buffs.end())
{
m_buffs.erase(i);
}
m_persistedBuffs.erase(buffNameCrc);
reapply = true;
}
newStackCount = std::max(0, std::min(newStackCount, static_cast<int>(BuffManager::getBuffMaxStacks(buffNameCrc))));
Buff::PackedBuff packedValue = Buff(buffNameCrc, timestamp, dynamicValue, static_cast<uint32>(totalDuration), caster, newStackCount).getPackedBuffValue();
m_buffs.insert(buffNameCrc, packedValue);
if (BuffManager::getIsBuffPersisted(buffNameCrc))
{
m_persistedBuffs.insert(buffNameCrc, packedValue);
}
if(reapply)
BuffManager::reapplyBuffEffects(this, buffNameCrc, timeTillRemoval, dynamicValue, newStackCount, caster);
else
BuffManager::applyBuffEffects(this, buffNameCrc, timeTillRemoval, dynamicValue, newStackCount, caster);
//If there was a state, add that state to this creature
int buffState = BuffManager::getBuffState(buffNameCrc);
if(buffState != -1)
setState(static_cast<States::Enumerator>(buffState), true);
}
// ----------------------------------------------------------------------
void CreatureObject::removeBuff(uint32 buffNameCrc)
{
if (!isAuthoritative())
{
sendControllerMessageToAuthServer(CM_removeBuff, new MessageQueueGenericValueType<int>(static_cast<int>(buffNameCrc)));
return;
}
std::map<uint32, Buff::PackedBuff>::const_iterator i = m_buffs.find(buffNameCrc);
if (i != m_buffs.end())
{
NetworkId caster = (*i).second.caster;
m_buffs.erase(i);
int buffState = BuffManager::getBuffState(buffNameCrc);
BuffManager::removeBuffEffects(this, buffNameCrc, caster);
if (!stillHasBuffState(buffNameCrc, buffState))
setState(static_cast<States::Enumerator>(buffState), false);
std::string const &callback = BuffManager::getBuffCallback(buffNameCrc);
ScriptParams params;
params.addParam(static_cast<int>(buffNameCrc), "buffCrc");
ScriptDictionaryPtr dictionary;
getScriptObject()->makeScriptDictionary(params, dictionary);
dictionary->serialize();
getScriptObject()->handleMessage(callback, dictionary);
}
// clean up any persistance
m_persistedBuffs.erase(buffNameCrc);
}
// ----------------------------------------------------------------------
void CreatureObject::decrementBuff(uint32 const buffNameCrc, uint32 const stacksToRemove)
{
std::map<const uint32, Buff::PackedBuff>::const_iterator i = m_buffs.find(buffNameCrc);
if (i != m_buffs.end())
{
Buff::PackedBuff b = (*i).second;
b.stackCount -= stacksToRemove;
removeBuff(buffNameCrc);
if (static_cast<int32>(b.stackCount) > 0)
{
uint32 currentTime = internalGetPlayedTime();
if (BuffManager::getBuffIsCelestial(buffNameCrc))
{
currentTime = ServerClock::getInstance().getGameTimeSeconds();
}
float endTime = static_cast<float> (b.endtime) - static_cast<float> (currentTime);
addBuff( buffNameCrc, endTime , b.value, static_cast<float> (b.duration), b.caster, b.stackCount);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::decayBuff(uint32 buffNameCrc, float decayPercentage)
{
std::map<const uint32, Buff::PackedBuff>::const_iterator i = m_buffs.find(buffNameCrc);
if (i != m_buffs.end())
{
Buff::PackedBuff b = (*i).second;
float decayValue = b.duration * decayPercentage; // decayPercentage should be a value between 0.0 and 1.0
removeBuff(buffNameCrc);
uint32 currentTime = internalGetPlayedTime();
float endTime = (static_cast<float> (b.endtime) - decayValue) - static_cast<float> (currentTime);
if(endTime > 0.0f)
addBuff( buffNameCrc, endTime , b.value, static_cast<float> (b.duration), b.caster, b.stackCount);
}
}
uint32 CreatureObject::internalGetPlayedTime() const
{
if (isPlayerControlled())
{
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
return static_cast<uint32>(playerObject->getPlayedTime());
}
}
return static_cast<uint32>(ServerClock::getInstance().getGameTimeSeconds());
}
// ----------------------------------------------------------------------
// This functions expires buffs as needed
void CreatureObject::updateBuffs()
{
uint32 timestamp = internalGetPlayedTime();
std::list< Buff > buffsToRemove;
for (std::map<uint32, Buff::PackedBuff>::const_iterator i = m_buffs.begin(); i != m_buffs.end(); )
{
const std::pair<uint32, Buff::PackedBuff> & p = *i++;
Buff b(p.first, p.second);
if (BuffManager::getBuffIsCelestial(b.m_nameCrc))
{
timestamp = ServerClock::getInstance().getGameTimeSeconds();
}
else
{
timestamp = internalGetPlayedTime();
}
if (b.m_timestamp < timestamp)
{
buffsToRemove.push_back(b);
}
}
std::list< Buff >::iterator iter = buffsToRemove.begin();
for( ; iter != buffsToRemove.end(); ++iter)
{
removeBuff((*iter).m_nameCrc);
}
}
// ----------------------------------------------------------------------
bool CreatureObject::stillHasBuffState(uint32 buffNameCrc, int state) const
{
for (std::map<uint32, Buff::PackedBuff>::const_iterator i = m_buffs.begin(); i != m_buffs.end(); ++i)
{
const std::pair<uint32, Buff::PackedBuff> & p = *i;
if ((p.first != buffNameCrc) && (BuffManager::getBuffState(p.first) == state))
return true;
}
return false;
}
// ----------------------------------------------------------------------
void CreatureObject::getAllBuffs(stdvector<uint32>::fwd & buffCrcs) const
{
buffCrcs.clear();
buffCrcs.reserve(m_buffs.size());
for (std::map<uint32, Buff::PackedBuff>::const_iterator i = m_buffs.begin(); i != m_buffs.end(); ++i)
{
const std::pair<uint32, Buff::PackedBuff> & p = *i;
buffCrcs.push_back(p.first);
}
}
// ----------------------------------------------------------------------
bool CreatureObject::getBuff(uint32 buffNameCrc, Buff & buff) const
{
std::map<uint32, Buff::PackedBuff>::const_iterator found = m_buffs.find(buffNameCrc);
if (found == m_buffs.end())
return false;
buff.set((*found).first, (*found).second);
return true;
}
// ----------------------------------------------------------------------
bool CreatureObject::hasBuff(uint32 buffNameCrc) const
{
std::map<uint32, Buff::PackedBuff>::const_iterator found = m_buffs.find(buffNameCrc);
return (found != m_buffs.end());
}
// ----------------------------------------------------------------------
bool CreatureObject::isInTutorial() const
{
return NewbieTutorial::isInTutorial(this);
}
// ----------------------------------------------------------------------
bool CreatureObject::isJedi() const
{
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
return playerObject->isJedi();
else
return false;
}
// ----------------------------------------------------------------------
void CreatureObject::setLevel(int level)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
controller->appendMessage(
CM_setLevel,
0.0f,
new MessageQueueGenericValueType<int>(level),
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject == NULL)
{
// this is an ai, so just set the level
m_level = (int16) level;
}
else
{
LevelManager::LevelData levelData;
LevelManager::setLevelDataFromLevel(levelData, level);
setLevelData(levelData.currentLevel, levelData.currentLevelXp, levelData.currentHealth);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::recalculateLevel()
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
{
if (!isAuthoritative())
{
Controller *controller = getController();
if (controller)
{
controller->appendMessage(
CM_recalculateLevel,
0.0f,
GameControllerMessageFlags::SEND |
GameControllerMessageFlags::RELIABLE |
GameControllerMessageFlags::DEST_AUTH_SERVER);
}
}
else
{
LevelManager::LevelData levelData;
LevelManager::calculateLevelData(getLevelXp(), levelData);
setLevelData(levelData.currentLevel, levelData.currentLevelXp, levelData.currentHealth);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::setLevelData(int16 level, int levelXp, int health)
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
{
m_totalLevelXp = levelXp;
m_levelHealthGranted = health;
m_level = level;
GroupObject *group = getGroup();
if (group != 0)
{
group->setMemberLevel(getNetworkId(), level);
}
std::map<NetworkId, LfgCharacterData> const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData();
std::map<NetworkId, LfgCharacterData>::const_iterator iterFind = connectedCharacterLfgData.find(getNetworkId());
if ((iterFind != connectedCharacterLfgData.end()) && (iterFind->second.level != static_cast<int16>(level)))
ServerUniverse::setConnectedCharacterLevelData(getNetworkId(), static_cast<int16>(level));
// update and sync
computeTotalAttributes();
}
}
// ----------------------------------------------------------------------
void CreatureObject::levelChanged() const
{
return;
}
// ----------------------------------------------------------------------
void CreatureObject::setClientUsesAnimationLocomotion(bool const enabled)
{
m_clientUsesAnimationLocomotion = enabled;
}
// ----------------------------------------------------------------------
void CreatureObject::fixupPersistentBuffsAfterLoading()
{
// initialize persisted buffs
if(m_fixedupPersistentBuffsAfterLoading)
return; // don't want to do it twice
m_fixedupPersistentBuffsAfterLoading = true;
DEBUG_WARNING(isPlayerControlled() && PlayerCreatureController::getPlayerObject(this) && !PlayerCreatureController::getPlayerObject(this)->isInitialized(),
("CreatureObject::fixupPersistentBuffsAfterLoading trying to use an uninitialized player object"));
uint32 currentTime = internalGetPlayedTime();
std::vector<Buff> tempPersistedHolder;
for (std::map<uint32, Buff::PackedBuff>::const_iterator ip = m_persistedBuffs.begin(); ip != m_persistedBuffs.end(); ++ip)
{
Buff b((*ip).first, (*ip).second);
tempPersistedHolder.push_back(b);
}
m_persistedBuffs.clear();
for (std::vector<Buff>::const_iterator i = tempPersistedHolder.begin(); i != tempPersistedHolder.end(); ++i)
{
const Buff & b = (*i);
if (BuffManager::getBuffIsCelestial(b.m_nameCrc))
{
currentTime = ServerClock::getInstance().getGameTimeSeconds();
}
else
currentTime = internalGetPlayedTime();
if (b.m_timestamp > currentTime || b.m_timestamp == 0xFFFFFFFF)
{
float timeTillRemoval = -1.0f;
if (b.m_timestamp != 0xFFFFFFFF)
timeTillRemoval = static_cast<float>(b.m_timestamp - currentTime);
addBuff(b.m_nameCrc, timeTillRemoval, b.m_value, static_cast<float>(b.m_duration), b.m_caster, b.m_stackCount);
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::fixupLevelXpAfterLoading()
{
// initialize persisted buffs
if(m_fixedupLevelXpAfterLoading)
return; // don't want to do it twice
m_fixedupLevelXpAfterLoading = true;
DEBUG_FATAL(!isPlayerControlled(),
("CreatureObject::fixupLevelXpAfterLoading trying to be called for a non-player controlled object"));
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
DEBUG_FATAL(!playerObject,
("CreatureObject::fixupLevelXpAfterLoading trying to use a non-existent player object"));
DEBUG_FATAL(playerObject && !playerObject->isInitialized(),
("CreatureObject::fixupLevelXpAfterLoading trying to use an uninitialized player object"));
// The level XP and level are not persisted in the database, so when we
// are done loading, we will calculate the current level XP and level
if (playerObject && playerObject->isInitialized())
{
LevelManager::LevelData levelData;
// Level XP due to experience points
std::map<std::string, int> const & experienceMap = playerObject->getExperiencePoints();
std::map<std::string, int>::const_iterator i;
for (i = experienceMap.begin(); i != experienceMap.end(); ++i)
{
LevelManager::addXpToLevelData(levelData, i->first, i->second);
}
// Level XP due to skills
for (SkillList::const_iterator skillIter = m_skills.begin(); skillIter != m_skills.end(); ++skillIter)
{
SkillObject const * skill = *skillIter;
if (skill)
{
LevelManager::addSkillToLevelData(levelData, skill->getSkillName());
}
}
// Update the skill info directly (we do not want scripts triggered)
m_level = levelData.currentLevel;
m_previousLevel = levelData.currentLevel;
m_totalLevelXp = levelData.currentLevelXp;
m_levelHealthGranted = levelData.currentHealth;
levelChanged();
}
}
// ----------------------------------------------------------------------
CreatureObject::Difficulty CreatureObject::getDifficulty() const
{
return static_cast<Difficulty>(m_difficulty.get());
}
// ----------------------------------------------------------------------
void CreatureObject::setDifficulty(Difficulty const difficulty)
{
m_difficulty = static_cast<unsigned char>(difficulty);
}
// ----------------------------------------------------------------------
void CreatureObject::setHologramType(int32 const newHologramType)
{
if (isAuthoritative())
{
m_hologramType = newHologramType;
}
else
{
sendControllerMessageToAuthServer(CM_modifyHologramType, new MessageQueueGenericValueType<int32 >(newHologramType));
}
}
// ----------------------------------------------------------------------
void CreatureObject::setVisibleOnMapAndRadar(bool const newVal)
{
if (isAuthoritative())
{
m_visibleOnMapAndRadar = newVal;
}
else
{
sendControllerMessageToAuthServer(CM_modifyVisibleOnMapAndRadar, new MessageQueueGenericValueType<bool>(newVal));
}
}
// ----------------------------------------------------------------------
float CreatureObject::getRegenRate(Attributes::Enumerator poolAttrib) const
{
if (!Attributes::isAttribPool(poolAttrib))
return 0;
// we've stored the rate as an int, convert back to float
int fixedFloatRate = m_attributes[poolAttrib+1];
return static_cast<float>(fixedFloatRate);
}
// ----------------------------------------------------------------------
void CreatureObject::setRegenRate(Attributes::Enumerator poolAttrib, float value)
{
if (Attributes::isAttribPool(poolAttrib))
{
if (value < 0)
value = 0;
if (isAuthoritative())
{
// store the value as an int
m_attributes.set(poolAttrib+1, static_cast<int>(floor(value)));
}
else
{
sendControllerMessageToAuthServer(CM_setRegenRate, new MessageQueueGenericValueType<std::pair<int, float> >(std::make_pair(static_cast<int>(poolAttrib), value)));
}
}
}
// ----------------------------------------------------------------------
void CreatureObject::setLastWaterDamageTime(unsigned long newTime)
{
m_lastWaterDamageTime = newTime;
}
// ----------------------------------------------------------------------
bool CreatureObject::doesLocomotionInvalidateCommand(Command const &cmd) const
{
Locomotions::Enumerator locomotion = getLocomotion();
if((locomotion != Locomotions::Running) || cmd.m_locomotionPermissions[locomotion])
return !cmd.m_locomotionPermissions[locomotion];
if(!isPlayerControlled())
return true;
PlayerCreatureController const *playerCreatureController = safe_cast<PlayerCreatureController const *>(getController());
if(playerCreatureController)
return (playerCreatureController->getLastSpeed() > ConfigSharedGame::getMovementThresholdForActionValidation());
return true;
}
// ----------------------------------------------------------------------
CommandQueue * CreatureObject::getCommandQueue() const
{
if (m_commandQueue == NULL)
{
m_commandQueue = CommandQueue::getCommandQueue(*const_cast<CreatureObject *>(this));
}
return m_commandQueue;
}
// ----------------------------------------------------------------------
void CreatureObject::clearCommands()
{
m_commands.clear();
}
// ----------------------------------------------------------------------
std::map<std::string, int> const & CreatureObject::getCommandList() const
{
return m_commands.getMap();
}
// ----------------------------------------------------------------------
/**
Multiple skills can grant the same command. Commands can be granted
apart from skills. When a skill is removed, it must remove the command
it granted, but it probably shouldn't remove the command if is granted
by another skill or commands granted via other methods
(items/quests, etc...)
*/
bool CreatureObject::grantCommand(std::string const & commandName, bool const fromSkill)
{
bool result = false;
if(isAuthoritative())
{
result = true;
// check if we already have the command
Archive::AutoDeltaMap<std::string, int>::const_iterator found = m_commands.find(commandName);
if (found == m_commands.end())
{
m_commands.set(commandName, 1);
if(getClient())
{
GrantCommand grant(commandName);
getClient()->send(grant, true);
}
if (!fromSkill)
{
// save command as objvar
setObjVarItem(OBJVAR_NOT_SKILL_COMMANDS + "." + commandName, 1);
}
}
else
{
m_commands.set(commandName, (*found).second + 1);
}
}
else
{
result = true;
sendControllerMessageToAuthServer(CM_grantCommand, new MessageQueueGenericValueType<std::string>(commandName));
}
return result;
}
//-----------------------------------------------------------------------
void CreatureObject::revokeCommand(std::string const & command, bool const fromSkill, bool const ignoreCount)
{
if(isAuthoritative())
{
Archive::AutoDeltaMap<std::string, int>::const_iterator found = m_commands.find(command);
if (found != m_commands.end())
{
if ((*found).second > 1 && !ignoreCount)
{
m_commands.set(command, (*found).second - 1);
}
else
{
m_commands.erase(found);
if(getClient())
{
RevokeCommand revoke(command);
getClient()->send(revoke, true);
}
if (!fromSkill)
{
// remove commands from objvars
removeObjVarItem(OBJVAR_NOT_SKILL_COMMANDS + "." + command);
}
}
}
}
else
{
sendControllerMessageToAuthServer(CM_revokeCommand, new MessageQueueGenericValueType<std::string>(command));
}
}
//-----------------------------------------------------------------------
bool CreatureObject::hasCommand(std::string const & commandName) const
{
bool result = false;
if (commandName == "unskilled")
result = true;
else
{
const Archive::AutoDeltaMap<std::string, int>::const_iterator f = m_commands.find(commandName);
result = (f != m_commands.end());
}
return result;
}
//-----------------------------------------------------------------------
/**
* Called when our observer count changes. Will bring us into or out of hibernation
* if needed.
*/
void CreatureObject::observersCountChanged()
{
if (isAuthoritative())
{
CreatureController * controller = getCreatureController();
int observers = getObserversCount();
bool hibernating = controller->getHibernate();
if ((observers > 0 && hibernating) ||
(observers <= 0 && !hibernating))
{
// attempt to change our hibernate state
controller->updateHibernate();
}
}
}
//-----------------------------------------------------------------------
bool CreatureObject::isStopped() const
{
return (m_stopWalkRun.get() == 0);
}
//-----------------------------------------------------------------------
bool CreatureObject::isWalking() const
{
return (m_stopWalkRun.get() == 1);
}
//-----------------------------------------------------------------------
bool CreatureObject::isRunning() const
{
return (m_stopWalkRun.get() == 2);
}
//-----------------------------------------------------------------------
void CreatureObject::setLookAtYaw(const float lookAtYaw, bool useLookAtYaw)
{
if(useLookAtYaw && (m_lookAtYaw != lookAtYaw))
setTransformChanged(true);
m_lookAtYaw = lookAtYaw;
m_useLookAtYaw = useLookAtYaw;
}
//-----------------------------------------------------------------------
float CreatureObject::getLookAtYaw() const
{
return m_lookAtYaw;
}
//-----------------------------------------------------------------------
bool CreatureObject::getUseLookAtYaw() const
{
return m_useLookAtYaw;
}
//-----------------------------------------------------------------------
int CreatureObject::getExpertiseRankForPlayer(std::string const & expertiseName)
{
int result = 0;
int rankMax = ExpertiseManager::getExpertiseRankMax(expertiseName);
int tree = ExpertiseManager::getExpertiseTree(expertiseName);
int tier = ExpertiseManager::getExpertiseTier(expertiseName);
int grid = ExpertiseManager::getExpertiseGrid(expertiseName);
int rank = 1;
while (rank <= rankMax)
{
SkillObject const * skill = ExpertiseManager::getExpertiseSkillAt(tree, tier, grid, rank);
if (skill && hasSkill(*skill))
{
result = rank;
rank++;
}
else
{
break;
}
}
return result;
}
//-----------------------------------------------------------------------
void CreatureObject::getExpertisesForPlayer(SkillList & expertiseList) const
{
expertiseList.clear();
SkillList const & skillList = this->getSkillList();
for (SkillList::const_iterator i = skillList.begin(); i != skillList.end(); ++i)
{
if (*i)
{
if (ExpertiseManager::isExpertise(*i))
{
expertiseList.insert(*i);
}
}
}
}
//-----------------------------------------------------------------------
int CreatureObject::getExpertisePointsSpentForPlayerInTree(int tree)
{
int spentPoints = 0;
SkillList expertiseList;
getExpertisesForPlayer(expertiseList);
for (SkillList::const_iterator i = expertiseList.begin(); i != expertiseList.end(); ++i)
{
SkillObject const * expertise = *i;
if (expertise)
{
std::string const & expertiseName = expertise->getSkillName();
if (ExpertiseManager::getExpertiseTree(expertiseName) == tree)
{
spentPoints += getExpertiseRankForPlayer(expertiseName);
}
}
}
return spentPoints;
}
//----------------------------------------------------------------------
int CreatureObject::getRemainingExpertisePoints() const
{
int const numPointsAvail = ExpertiseManager::getExpertisePointsForLevel(getLevel());
CreatureObject::SkillList expertiseList;
getExpertisesForPlayer(expertiseList);
int const numPointsUsed = expertiseList.size() - 1; // subtract 1 for skill "expertise"
return numPointsAvail - numPointsUsed;
}
//-----------------------------------------------------------------------
bool CreatureObject::processExpertiseRequest(std::vector<std::string> const &addExpertisesNamesList, bool clearAllExpertisesFirst)
{
for(std::vector<std::string>::const_iterator i = addExpertisesNamesList.begin(); i != addExpertisesNamesList.end(); ++i)
{
std::string const &s = *i;
const SkillObject *skill = SkillManager::getInstance().getSkill(s);
if(skill)
{
//Check prerequisites
SkillObject::SkillVector const prereqs = skill->getPrerequisiteSkills();
for (SkillObject::SkillVector::const_iterator i = prereqs.begin(); i != prereqs.end(); ++i)
{
SkillObject const * prereq = (*i);
if (!prereq || !hasSkill(*prereq))
{
DEBUG_WARNING(true, ("player %s tried to get expertise %s but doesn't have expertise %s", getNetworkId().getValueString().c_str(),
skill->getSkillName().c_str(), prereq->getSkillName().c_str()));
return false;
}
}
//Check tier points
//Check if the player has enough points for a skill of this tier
int tree = ExpertiseManager::getExpertiseTree(s);
int pointsInTree = getExpertisePointsSpentForPlayerInTree(tree);
int tier = ExpertiseManager::getExpertiseTier(s);
if (pointsInTree < (tier - 1) * POINTS_PER_TIER)
{
DEBUG_WARNING(true, ("player %s tried to get expertise %s but only has %d points in tree %d, needs %d", getNetworkId().getValueString().c_str(),
s.c_str(), pointsInTree, tree, (tier - 1) * POINTS_PER_TIER
));
return false;
}
if (getRemainingExpertisePoints() < 1)
{
LOG("CustomerService",
("SuspectedCheaterChannel: %s attempted to gain more expertise than allowed.",
PlayerObject::getAccountDescription(this).c_str())
);
return false;
}
//Check faction column
//Grant skill
grantSkill(*skill);
}
}
return true;
}
//-----------------------------------------------------------------------
bool CreatureObject::clearAllExpertises()
{
int numTries = 0;
while(numTries < 50)
{
//@TODO: guard this however design wants
const SkillList &skills = getSkillList();
std::vector<const SkillObject *> skillsToRevoke;
for(std::set<const SkillObject *>::iterator i = skills.begin(); i != skills.end(); ++i)
{
const SkillObject *skill = *i;
if(skill && ExpertiseManager::isExpertise(skill))
skillsToRevoke.push_back(skill);
}
if(skillsToRevoke.empty())
return true;
for(std::vector<const SkillObject *>::iterator i2 = skillsToRevoke.begin(); i2 != skillsToRevoke.end(); ++i2)
{
const SkillObject *skill = *i2;
revokeSkill(*skill, true);
}
numTries++;
}
return false;
}
//-----------------------------------------------------------------------
void CreatureObject::recomputeCommandSeries()
{
for(std::vector<CommandSeriesRecord>::iterator i = s_commandSeriesRecords.begin(); i != s_commandSeriesRecords.end(); ++i)
{
CommandSeriesRecord const & csr = *i;
bool hasLevel = getLevel() >= csr.level;
if(csr.baseCommandName.empty())
{
if(!hasLevel)
LOG("CustomerService",("Player %s of level %d has command %s from command series, but should not have it until level %d",
getNetworkId().getValueString().c_str(), getLevel(), csr.baseCommandName.c_str(), csr.level));
}
else
{
if(hasCommand(csr.commandName) && (!hasCommand(csr.baseCommandName) || !hasLevel))
{
revokeCommand(csr.commandName, true);
}
else if(!hasCommand(csr.commandName) && hasCommand(csr.baseCommandName) && hasLevel)
{
grantCommand(csr.commandName, true);
}
}
}
}
//-----------------------------------------------------------------------
void CreatureObject::incrementKillMeter(int amount)
{
if (isPlayerControlled())
{
PlayerObject * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject != NULL)
{
playerObject->incrementKillMeter(amount);
}
}
}
//-----------------------------------------------------------------------
void CreatureObject::getLfgCharacterData(LfgCharacterData & lfgCharacterData) const
{
lfgCharacterData.characterId = getNetworkId();
lfgCharacterData.characterName = getAssignedObjectName();
lfgCharacterData.characterLowercaseFirstName = Unicode::wideToNarrow(Unicode::toLower(getAssignedObjectFirstName()));
lfgCharacterData.species = getSpecies();
lfgCharacterData.gender = getGender();
std::map<NetworkId, LfgCharacterData> const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData();
std::map<NetworkId, LfgCharacterData>::const_iterator iterFind = connectedCharacterLfgData.find(getNetworkId());
if (iterFind != connectedCharacterLfgData.end())
{
lfgCharacterData.characterInterests = iterFind->second.characterInterests;
lfgCharacterData.characterCreateTime = iterFind->second.characterCreateTime;
lfgCharacterData.characterAge = iterFind->second.characterAge;
}
else
{
lfgCharacterData.characterInterests.clear();
lfgCharacterData.characterCreateTime = -1;
lfgCharacterData.characterAge = -1;
}
if (lfgCharacterData.characterCreateTime <= 0)
lfgCharacterData.characterCreateTime = static_cast<int32>(NameManager::getInstance().getPlayerCreateTime(getNetworkId()));
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this);
if (playerObject)
{
lfgCharacterData.characterAge = playerObject->getAgeInDays();
lfgCharacterData.profession = LfgCharacterData::convertSkillTemplateToProfession(playerObject->getSkillTemplate());
MatchMakingId const & id = playerObject->getMatchMakingCharacterProfileId();
lfgCharacterData.anonymous = id.isBitSet(MatchMakingId::B_anonymous);
lfgCharacterData.searchableByCtsSourceGalaxy = id.isBitSet(MatchMakingId::B_searchableByCtsSourceGalaxy);
lfgCharacterData.displayLocationInSearchResults = id.isBitSet(MatchMakingId::B_displayLocationInSearchResults);
if (id.isBitSet(MatchMakingId::B_lookingForGroup))
LfgDataTable::setBit("lfg", lfgCharacterData.characterInterests);
else
LfgDataTable::clearBit("lfg", lfgCharacterData.characterInterests);
if (id.isBitSet(MatchMakingId::B_helper))
LfgDataTable::setBit("helper", lfgCharacterData.characterInterests);
else
LfgDataTable::clearBit("helper", lfgCharacterData.characterInterests);
if (id.isBitSet(MatchMakingId::B_rolePlay))
LfgDataTable::setBit("rp", lfgCharacterData.characterInterests);
else
LfgDataTable::clearBit("rp", lfgCharacterData.characterInterests);
if (id.isBitSet(MatchMakingId::B_lookingForWork))
LfgDataTable::setBit("lfw", lfgCharacterData.characterInterests);
else
LfgDataTable::clearBit("lfw", lfgCharacterData.characterInterests);
lfgCharacterData.active = (playerObject->getSessionLastActiveTime() != 0);
lfgCharacterData.sessionActivity = playerObject->getSessionActivity();
}
else
{
lfgCharacterData.profession = LfgCharacterData::Prof_Unknown;
lfgCharacterData.anonymous = false;
lfgCharacterData.displayLocationInSearchResults = false;
lfgCharacterData.active = false;
lfgCharacterData.sessionActivity = 0;
}
SkillObject const * skill = SkillManager::getInstance().getSkill("pilot_rebel_navy_novice");
if (skill && hasSkill(*skill))
{
lfgCharacterData.pilot = LfgCharacterData::Pilot_Rebel;
}
else
{
skill = SkillManager::getInstance().getSkill("pilot_imperial_navy_novice");
if (skill && hasSkill(*skill))
{
lfgCharacterData.pilot = LfgCharacterData::Pilot_Imperial;
}
else
{
skill = SkillManager::getInstance().getSkill("pilot_neutral_novice");
if (skill && hasSkill(*skill))
{
lfgCharacterData.pilot = LfgCharacterData::Pilot_Neutral;
}
else
{
lfgCharacterData.pilot = LfgCharacterData::Pilot_None;
}
}
}
lfgCharacterData.level = getLevel();
lfgCharacterData.faction = getPvpFaction();
int const guildId = getGuildId();
if (guildId == 0)
{
lfgCharacterData.guildName.clear();
lfgCharacterData.guildAbbrev.clear();
}
else
{
lfgCharacterData.guildName = GuildInterface::getGuildName(guildId);
lfgCharacterData.guildAbbrev = GuildInterface::getGuildAbbrev(guildId);
}
std::vector<int> const & cityIds = CityInterface::getCitizenOfCityId(getNetworkId());
if (!cityIds.empty())
{
lfgCharacterData.citizenOfCity = CityInterface::getCityInfo(cityIds.front()).getCityName();
}
else
{
lfgCharacterData.citizenOfCity.clear();
}
lfgCharacterData.groupId = m_group.get();
lfgCharacterData.locationPlanet = ServerWorld::getSceneId();
lfgCharacterData.locationRegion.clear();
lfgCharacterData.locationFactionalPresenceGcwRegion.clear();
lfgCharacterData.locationFactionalPresenceGridX = 0;
lfgCharacterData.locationFactionalPresenceGridZ = 0;
lfgCharacterData.locationPlayerCity.clear();
PlanetObject const * const planetObject = ServerUniverse::getInstance().getPlanetByName(lfgCharacterData.locationPlanet);
if (planetObject)
{
Vector const creaturePosition = findPosition_w();
Region const * const region = RegionMaster::getSmallestVisibleRegionAtPoint(planetObject->getName(), creaturePosition.x, creaturePosition.z);
if (region != NULL)
lfgCharacterData.locationRegion = Unicode::wideToNarrow(region->getName());
// handle factional presence
Client const * const client = getClient();
if (client && (!ConfigServerGame::getGcwFactionalPresenceDisableForSecuredLogin() || !client->isGodValidated()))
{
std::string const & playerCurrentGcwRegion = playerObject->getCurrentGcwRegion();
if (!playerCurrentGcwRegion.empty())
{
if (!ServerWorld::isSpaceScene())
{
// ground factional presence
//
// character contributes to ground GCW factional presence if he is actively
// playing the game, is declared factional, is not in a cell, is not
// cloaked/incapped/dead/swimming, and is not hidden from the overhead map/radar
if (lfgCharacterData.active && !PvpData::isNeutralFactionId(lfgCharacterData.faction) && PvpInternal::isDeclared(*this) && !isHidden() && isVisible() && getCoverVisibility() && !isIncapacitated() && !isDead() && !getState(States::Swimming) && getVisibleOnMapAndRadar())
{
bool inCell = false;
ServerObject const * o = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
while (o)
{
if (o->asCellObject())
{
inCell = true;
break;
}
o = safe_cast<ServerObject const*>(ContainerInterface::getContainedByObject(*o));
}
if (!inCell)
{
// make sure he (or his vehicle/mount) is standing on the
// ground, and not on a roof/balcony/storyteller platform/etc
Object const * playerOrMount = getMountedCreature();
if (playerOrMount)
{
// is factional presence allowed while mounted?
if (ConfigServerGame::getGcwFactionalPresenceMountedPct() <= 0)
playerOrMount = NULL;
}
else
{
playerOrMount = this;
}
if (playerOrMount)
{
CollisionProperty const * const collisionProperty = playerOrMount->getCollisionProperty();
Footprint const * const footprint = (collisionProperty ? collisionProperty->getFootprint() : NULL);
bool isOnSolidFloor = (footprint && footprint->isOnSolidFloor());
if (isOnSolidFloor)
{
// make sure the player/mount is actually standing on the floor for it to count
float groundHeight, floorHeight;
if (footprint->getGroundHeight(groundHeight) && footprint->getFloorHeight(floorHeight) && (groundHeight == floorHeight))
{
// really standing on the floor
}
else
{
// not really standing on the floor
isOnSolidFloor = false;
}
}
// standing on the ground
if (!isOnSolidFloor)
{
lfgCharacterData.locationFactionalPresenceGcwRegion = playerCurrentGcwRegion;
// if there's factional presence, calculate the grid in which it is occurring
// clamp to -8000 - 7999
int posX = std::min(std::max(static_cast<int>(creaturePosition.x), -8000), 7999);
// clamp to -8000 - 7999
int posZ = std::min(std::max(static_cast<int>(creaturePosition.z), -8000), 7999);
if (posX < 0)
posX -= (PvpData::getGcwFactionalPresenceGridSize() - 1);
if (posZ < 0)
posZ -= (PvpData::getGcwFactionalPresenceGridSize() - 1);
lfgCharacterData.locationFactionalPresenceGridX = ((posX / PvpData::getGcwFactionalPresenceGridSize()) * PvpData::getGcwFactionalPresenceGridSize()) + (PvpData::getGcwFactionalPresenceGridSize() / 2);
lfgCharacterData.locationFactionalPresenceGridZ = ((posZ / PvpData::getGcwFactionalPresenceGridSize()) * PvpData::getGcwFactionalPresenceGridSize()) + (PvpData::getGcwFactionalPresenceGridSize() / 2);
}
}
}
}
}
else if (lfgCharacterData.active && !isIncapacitated() && !isDead())
{
// space factional presence
//
// character contributes to space GCW factional presence if he is actively playing the
// game, is not incapped/dead, is on board a player ship that's open to attack from
// other player ships, and is the same faction as the ship
ShipObject const * const ship = ShipObject::getContainingShipObject(this);
if (ship && ship->isPlayerShip() && !ship->isInvulnerable() && PvpInternal::isDeclared(*ship))
{
uint32 const shipFaction = ship->getSpaceFaction();
if ((PvpData::isImperialFactionId(shipFaction) || PvpData::isRebelFactionId(shipFaction)) && (lfgCharacterData.faction == shipFaction))
lfgCharacterData.locationFactionalPresenceGcwRegion = playerCurrentGcwRegion;
}
}
}
}
int const cityId = CityInterface::getCityAtLocation(planetObject->getName(), static_cast<int>(creaturePosition.x), static_cast<int>(creaturePosition.z), 0);
if (cityId > 0)
lfgCharacterData.locationPlayerCity = CityInterface::getCityInfo(cityId).getCityName();
}
// CTS transferred from galaxy
lfgCharacterData.ctsSourceGalaxy.clear();
if (getObjVars().hasItem("ctsHistory"))
{
DynamicVariableList::NestedList const ctsTransactions(getObjVars(), "ctsHistory");
for (DynamicVariableList::NestedList::const_iterator i = ctsTransactions.begin(); i != ctsTransactions.end(); ++i)
{
Unicode::String ctsTransactionDetail;
if (i.getValue(ctsTransactionDetail))
{
Unicode::UnicodeStringVector tokens;
if (Unicode::tokenize(ctsTransactionDetail, tokens, NULL, NULL) && (tokens.size() >= 4))
{
IGNORE_RETURN(lfgCharacterData.ctsSourceGalaxy.insert(Unicode::wideToNarrow(tokens[1])));
}
}
}
}
}
// ----------------------------------------------------------------------
bool CreatureObjectNamespace::GroupHelpers::creatureIsContainedInPOBShip(CreatureObject const * const creatureObject)
{
NOT_NULL(creatureObject);
ShipObject const * const shipObject = ShipObject::getContainingShipObject(creatureObject);
return shipObject && shipObject->getPortalProperty();
}
// ----------------------------------------------------------------------
void CreatureObjectNamespace::GroupHelpers::findAllTargetsForGroup(CreatureObject * const targetObj, std::vector<CreatureObject *> & targets)
{
typedef std::vector<CreatureObject *> Targets;
NOT_NULL(targetObj);
ShipObject * const shipObject = ShipObject::getContainingShipObject(targetObj);
GroupObject const * const groupObject = targetObj->getGroup();
if ((shipObject != 0) && (groupObject != 0))
{
Targets passengers;
shipObject->findAllPassengers(passengers, true);
Targets::const_iterator ii = passengers.begin();
Targets::const_iterator iiEnd = passengers.end();
for (; ii != iiEnd; ++ii)
{
CreatureObject * const passenger = *ii;
if (passenger != 0)
{
NetworkId const & passengerId = passenger->getNetworkId();
if (groupObject->isGroupMember(passengerId))
{
targets.push_back(passenger);
}
}
NOT_NULL(passenger);
}
}
else
{
targets.push_back(targetObj);
}
}
// ----------------------------------------------------------------------
bool CreatureObjectNamespace::GroupHelpers::roomInGroup(GroupObject const * groupObj, int additionalMembers)
{
if (groupObj != 0)
{
return groupObj->doesGroupHaveRoomFor(additionalMembers);
}
additionalMembers = std::max(0, additionalMembers);
return additionalMembers < GroupObject::maximumMembersInGroup();
}
// ----------------------------------------------------------------------
GroupMemberParam const CreatureObjectNamespace::GroupHelpers::buildGroupMemberParam(CreatureObject const * const creatureObject)
{
NOT_NULL(creatureObject);
ShipObject const * const shipObject = ShipObject::getContainingShipObject(creatureObject);
NetworkId const & memberId = creatureObject->getNetworkId();
std::string const & name = Unicode::wideToNarrow(creatureObject->getEncodedObjectName());
int const level = creatureObject->getLevel();
bool const memberIsPC = creatureObject->isPlayerControlled();
LfgCharacterData::Profession profession = LfgCharacterData::Prof_Unknown;
if (!memberIsPC)
{
profession = LfgCharacterData::Prof_NPC;
}
else
{
PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(creatureObject);
if (playerObject)
profession = LfgCharacterData::convertSkillTemplateToProfession(playerObject->getSkillTemplate());
}
NetworkId const & shipId = (shipObject != 0) ? shipObject->getNetworkId() : NetworkId::cms_invalid;
bool const shipIsPOB = shipObject != 0 && shipObject->getPortalProperty() != 0;
bool const ownsPOB = shipObject != 0 && shipObject->getOwnerId() == memberId;
return GroupMemberParam(memberId, name, level, profession, memberIsPC, shipId, shipIsPOB, ownsPOB);
}
// ----------------------------------------------------------------------
void CreatureObjectNamespace::GroupHelpers::buildGroupMemberParamsFromCreatures(std::vector<CreatureObject *> const & targets, GroupObject::GroupMemberParamVector & targetMemberParams)
{
std::vector<CreatureObject *>::const_iterator ii = targets.begin();
std::vector<CreatureObject *>::const_iterator iiEnd = targets.end();
for (; ii != iiEnd; ++ii)
{
CreatureObject const * const creatureObject = *ii;
if (creatureObject != 0)
{
targetMemberParams.push_back(GroupHelpers::buildGroupMemberParam(creatureObject));
}
}
}
// ----------------------------------------------------------------------
void CreatureObjectNamespace::restoreItemDecorationLayout(CreatureObject & decorator, PlayerObject & decoratorPlayerObject, CachedNetworkId const & item, TangibleObject const & pob, CachedNetworkId const & cell, Vector const & position, Quaternion const & orientation)
{
// make sure item is around
ServerObject const * const itemSo = safe_cast<ServerObject const *>(item.getObject());
if (!itemSo)
return;
// make sure item is immediately in a cell/room
ServerObject const * const itemContainingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*itemSo));
if (!itemContainingCell || !itemContainingCell->asCellObject())
return;
// make sure item is in the pob
if (&pob != ContainerInterface::getContainedByObject(*itemContainingCell))
return;
// make sure the cell is around
ServerObject const * const cellSo = safe_cast<ServerObject const *>(cell.getObject());
if (!cellSo)
return;
CellObject const * const cellCo = cellSo->asCellObject();
if (!cellCo)
return;
// make sure cell is in the pob
if (&pob != ContainerInterface::getContainedByObject(*cellCo))
return;
// item is not currently in the target room, need to move it
bool needToMoveItem = false;
bool needToRotateItem = false;
Transform const * itemCurrentTransform = NULL;
if (itemContainingCell->getNetworkId() != cellSo->getNetworkId())
{
needToMoveItem = true;
}
else
{
// if the item is not currently at the desired position, need to move it
itemCurrentTransform = &(itemSo->getTransform_o2p());
Vector const currentPosition = itemCurrentTransform->getPosition_p();
if (currentPosition.magnitudeBetweenSquared(position) > 0.0025) // not within 0.05m
{
needToMoveItem = true;
}
else
{
// if the item is not currently at the desired orientation, need to rotate it
Quaternion const currentOrientation(*itemCurrentTransform);
if (!WithinEpsilonInclusive(currentOrientation.w, orientation.w, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.x, orientation.x, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.y, orientation.y, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.z, orientation.z, 0.01f))
{
needToRotateItem = true;
}
}
}
if (!needToMoveItem && !needToRotateItem)
return;
// if the item needs to be moved, check to see if it needs to be rotated as well
if (needToMoveItem)
{
if (!itemCurrentTransform)
itemCurrentTransform = &(itemSo->getTransform_o2p());
Quaternion const currentOrientation(*itemCurrentTransform);
if (!WithinEpsilonInclusive(currentOrientation.w, orientation.w, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.x, orientation.x, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.y, orientation.y, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.z, orientation.z, 0.01f))
{
needToRotateItem = true;
}
}
// if the item needs to be moved, find the "best" valid position for the item
if (needToMoveItem)
{
CellProperty const * const cellProperty = cellCo->getCellProperty();
if (!cellProperty)
return;
Floor const * const floor = cellProperty->getFloor();
if (!floor)
return;
BaseExtent const * const floorExtent = floor->getExtent_l();
if (!floorExtent)
return;
AxialBox const floorExtentBoundingBox = floorExtent->getBoundingBox();
Vector floorExtentBoundingBoxCenter = floorExtentBoundingBox.getCenter();
floorExtentBoundingBoxCenter.y = position.y + 1.0f;
Vector goodLocation(position);
goodLocation.y = floorExtentBoundingBoxCenter.y;
bool foundGoodPoint = true;
FloorLocator flResult;
if (!floor->intersectClosest(Ray3d(goodLocation, Vector::negativeUnitY), flResult))
{
// do a "binary" search of points on the line starting at the floor bounding
// box center location (which may be a good location) and ending at the target
// location (which is a bad location) until a point on the line is found
// that is a good location, and that point is "up against" the wall of the cell
Vector badLocation(goodLocation);
goodLocation = floorExtentBoundingBoxCenter;
foundGoodPoint = floor->intersectClosest(Ray3d(goodLocation, Vector::negativeUnitY), flResult);
Vector midpointPosition;
while (true)
{
// stop when the 2 closest "good" and "bad" location points are .2m apart,
// meaning we have gotten close enough to "up against" the wall of the cell
if (goodLocation.magnitudeXZBetweenSquared(badLocation) <= 0.04f)
break;
midpointPosition = Vector::midpoint(goodLocation, badLocation);
midpointPosition.y = floorExtentBoundingBoxCenter.y;
if (floor->intersectClosest(Ray3d(midpointPosition, Vector::negativeUnitY), flResult))
{
foundGoodPoint = true;
goodLocation = midpointPosition;
}
else
{
badLocation = midpointPosition;
}
}
}
// couldn't find any valid position for the item
if (!foundGoodPoint)
return;
// set the height of the item
goodLocation.y = position.y;
// trigger script to move the item, which will perform the remaining validation
GameScriptObject * const gameScriptObject = decorator.getScriptObject();
if (!gameScriptObject)
return;
ScriptParams sp;
sp.addParam(itemSo->getNetworkId());
sp.addParam(cellSo->getNetworkId());
sp.addParam(goodLocation);
sp.addParam(needToRotateItem);
sp.addParam(orientation.w);
sp.addParam(orientation.x);
sp.addParam(orientation.y);
sp.addParam(orientation.z);
// set the id of the object being restored, so that we can bypass the LOS check for it
decoratorPlayerObject.setRestoreDecorationObjectBeingRestored(itemSo->getNetworkId());
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_DO_RESTORE_ITEM_DECORATION_LAYOUT, sp));
decoratorPlayerObject.setRestoreDecorationObjectBeingRestored(NetworkId::cms_invalid);
// check to see if the item got restored
bool itemGotRestored = true;
if (itemContainingCell->getNetworkId() == cellSo->getNetworkId())
{
Transform const & itemCurrentTransform = itemSo->getTransform_o2p();
Vector const currentPosition = itemCurrentTransform.getPosition_p();
if (currentPosition.magnitudeBetweenSquared(goodLocation) > 0.0025) // not within 0.05m
{
itemGotRestored = false;
}
else if (needToRotateItem)
{
Quaternion const currentOrientation(itemCurrentTransform);
if (!WithinEpsilonInclusive(currentOrientation.w, orientation.w, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.x, orientation.x, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.y, orientation.y, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.z, orientation.z, 0.01f))
{
itemGotRestored = false;
}
}
}
else if (cellSo != ContainerInterface::getContainedByObject(*itemSo))
{
itemGotRestored = false;
}
if (itemGotRestored)
decoratorPlayerObject.setRestoreDecorationSuccessCount(decoratorPlayerObject.getRestoreDecorationSuccessCount() + 1);
}
else if (needToRotateItem)
{
// trigger script to rotate the item, which will perform the remaining validation
GameScriptObject * const gameScriptObject = decorator.getScriptObject();
if (!gameScriptObject)
return;
ScriptParams sp;
sp.addParam(itemSo->getNetworkId());
sp.addParam(orientation.w);
sp.addParam(orientation.x);
sp.addParam(orientation.y);
sp.addParam(orientation.z);
// set the id of the object being restored, so that we can bypass the LOS check for it
decoratorPlayerObject.setRestoreDecorationObjectBeingRestored(itemSo->getNetworkId());
IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_DO_RESTORE_ITEM_DECORATION_LAYOUT_ROTATION_ONLY, sp));
decoratorPlayerObject.setRestoreDecorationObjectBeingRestored(NetworkId::cms_invalid);
// check to see if the item got restored
bool itemGotRestored = true;
Transform const & itemCurrentTransform = itemSo->getTransform_o2p();
Quaternion const currentOrientation(itemCurrentTransform);
if (!WithinEpsilonInclusive(currentOrientation.w, orientation.w, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.x, orientation.x, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.y, orientation.y, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.z, orientation.z, 0.01f))
{
itemGotRestored = false;
}
if (itemGotRestored)
decoratorPlayerObject.setRestoreDecorationSuccessCount(decoratorPlayerObject.getRestoreDecorationSuccessCount() + 1);
}
}
// ----------------------------------------------------------------------
void CreatureObject::clientMinigameOpen(ValueDictionary const & messageData)
{
typedef ValueDictionary Payload;
MessageQueueGenericValueType<Payload> * msg = new MessageQueueGenericValueType<Payload>(messageData);
if (isAuthoritative())
{
appendMessage(CM_clientMinigameOpen, 0.0f, msg, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
else
{
sendControllerMessageToAuthServer(CM_clientMinigameOpen, msg);
}
}
// ----------------------------------------------------------------------
void CreatureObject::clientMinigameClose(ValueDictionary const & messageData)
{
typedef ValueDictionary Payload;
MessageQueueGenericValueType<Payload> * msg = new MessageQueueGenericValueType<Payload>(messageData);
if (isAuthoritative())
{
appendMessage(CM_clientMinigameClose, 0.0f, msg, GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_CLIENT);
}
else
{
sendControllerMessageToAuthServer(CM_clientMinigameClose, msg);
}
}
// ----------------------------------------------------------------------
void CreatureObject::addPackedAppearanceWearable(std::string const &appearanceData, int arrangementIndex, NetworkId const &networkId, uint32 sharedTemplateCrcValue)
{
//-- Check if this network id already exists in the wearable list.
size_t const length = m_wearableAppearanceData.size();
for (size_t i = 0; i < length; ++i)
{
WearableEntry const &entry = m_wearableAppearanceData[i];
if (entry.m_networkId == networkId)
{
// Check if this entry exists but in a different configuration.
WARNING((entry.m_arrangement != arrangementIndex), ("attempted to add object id=[%s] in packed wearable list multiple times in different arrangements, existing=[%d],new=[%d].", appearanceData.c_str(), entry.m_arrangement, arrangementIndex));
WARNING((static_cast<uint32>(entry.m_objectTemplate) != sharedTemplateCrcValue), ("attempted to add object id=[%s] in packed wearable list multiple times differing only by shared object template crc value, existing=[%d],new=[%d].", appearanceData.c_str(), entry.m_objectTemplate, static_cast<int>(sharedTemplateCrcValue)));
DEBUG_WARNING((entry.m_appearanceString != appearanceData), ("attempted to add object id=[%s] in packed wearable list multiple times differing only by appearance customization data.", appearanceData.c_str()));
return;
}
}
//-- Add the new entry.
m_wearableAppearanceData.push_back(WearableEntry(appearanceData, arrangementIndex, networkId, sharedTemplateCrcValue, NULL, NULL));
}
// ----------------------------------------------------------------------
void CreatureObject::saveDecorationLayout(ServerObject const & pobSourceObject, int saveSlotNumber, std::string const & description)
{
int debugNumItems = 0;
const unsigned long debugStartTimeMs = Clock::timeMs();
if (!isAuthoritative())
return;
if (!getClient())
return;
// only slots 1-3 are currently supported
if ((saveSlotNumber < 1) || (saveSlotNumber > 3))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
if (!pobSourceObject.isAuthoritative())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
PlayerObject * const playerObj = PlayerCreatureController::getPlayerObject(this);
if (!playerObj)
return;
ServerObject const * const containingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
if (!containingCell || !containingCell->asCellObject())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
ServerObject const * const containingPOBso = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*containingCell));
if (!containingPOBso)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
if (!containingPOBso->isInWorldCell())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
if (containingPOBso != &pobSourceObject)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
if (containingPOBso->asBuildingObject() && !containingPOBso->asBuildingObject()->getContentsLoaded())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("Please wait until the building has loaded its contents and try again."), Unicode::emptyString);
return;
}
TangibleObject const * const containingPOB = containingPOBso->asTangibleObject();
if (!containingPOB)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
if (!containingPOB->isOnAdminList(*this))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be the owner or an admin of the structure or POB ship to do this."), Unicode::emptyString);
return;
}
Container const * const pobContainer = ContainerInterface::getContainer(*containingPOB);
if (pobContainer)
{
std::string objvarValue;
int objvarValueIndex = 0;
char buffer1[64];
buffer1[sizeof(buffer1)-1] = '\0';
char buffer2[1024];
buffer2[sizeof(buffer2)-1] = '\0';
std::set<std::string> rooms;
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d", saveSlotNumber);
playerObj->removeObjVarItem(buffer1);
for (ContainerConstIterator i = pobContainer->begin(); i != pobContainer->end(); ++i)
{
ServerObject const * const cell = safe_cast<ServerObject const *>((*i).getObject());
if (cell && cell->asCellObject())
{
CellProperty const * const cellContainer = cell->getCellProperty();
if (cellContainer)
{
std::map<std::pair<uint32, uint32>, std::pair<float, float> >::const_iterator const iterFindCellHeight = s_mapCellHeightRestriction.find(std::make_pair(containingPOB->getTemplateCrc(), cellContainer->getCellNameCrc()));
if (iterFindCellHeight == s_mapCellHeightRestriction.end())
continue;
Floor const * const floor = cellContainer->getFloor();
if (!floor)
continue;
BaseExtent const * const floorExtent = floor->getExtent_l();
if (!floorExtent)
continue;
AxialBox const floorExtentBoundingBox = floorExtent->getBoundingBox();
Vector const floorExtentBoundingBoxCenter = floorExtentBoundingBox.getCenter();
for (ContainerConstIterator j = cellContainer->begin(); j != cellContainer->end(); ++j)
{
ServerObject const * const cellContent = safe_cast<ServerObject const *>((*j).getObject());
if (cellContent && cellContent->isPersisted() && !cellContent->asCreatureObject() && !cellContent->isVendor())
{
++debugNumItems;
IGNORE_RETURN(rooms.insert(cellContainer->getCellName()));
Transform const & transform = cellContent->getTransform_o2p();
Vector const position = transform.getPosition_p();
Quaternion const quaternion(transform);
snprintf(buffer2, sizeof(buffer2)-1, "%s~%s~%.10g~%.10g~%.10g~%.10g~%.10g~%.10g~%.10g", cellContent->getNetworkId().getValueString().c_str(), cellContainer->getCellName(), position.x - floorExtentBoundingBoxCenter.x, position.y - iterFindCellHeight->second.first, position.z - floorExtentBoundingBoxCenter.z, quaternion.w, quaternion.x, quaternion.y, quaternion.z);
if ((objvarValue.size() + ::strlen(buffer2) + 2) > DynamicVariable::MAX_DYNAMIC_VARIABLE_UTF8_LENGTH)
{
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.%d", saveSlotNumber, objvarValueIndex);
playerObj->setObjVarItem(buffer1, objvarValue);
++objvarValueIndex;
objvarValue = buffer2;
}
else
{
if (!objvarValue.empty())
objvarValue += "~";
objvarValue += buffer2;
}
}
}
}
}
}
if (rooms.empty())
{
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d", saveSlotNumber);
playerObj->removeObjVarItem(buffer1);
Chat::sendSystemMessage(*this, Unicode::narrowToWide("There is currently no decoration in the structure or POB ship to save."), Unicode::emptyString);
}
else
{
if (!objvarValue.empty())
{
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.%d", saveSlotNumber, objvarValueIndex);
playerObj->setObjVarItem(buffer1, objvarValue);
}
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.saveTime", saveSlotNumber);
playerObj->setObjVarItem(buffer1, static_cast<int>(::time(NULL)));
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.pobName", saveSlotNumber);
playerObj->setObjVarItem(buffer1, containingPOB->getObjectNameStringId().localize());
std::vector<Unicode::String> roomsVector;
for (std::set<std::string>::const_iterator iterRoom = rooms.begin(); iterRoom != rooms.end(); ++iterRoom)
roomsVector.push_back(Unicode::narrowToWide(*iterRoom));
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.rooms", saveSlotNumber);
playerObj->setObjVarItem(buffer1, roomsVector);
std::string validatedDescription = description;
if (!validatedDescription.empty())
{
if (validatedDescription.size() > 40)
validatedDescription = validatedDescription.substr(0, 40);
snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.description", saveSlotNumber);
playerObj->setObjVarItem(buffer1, validatedDescription);
}
if (validatedDescription.empty())
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("The decoration layout of the structure or POB ship has been saved in slot %d.", saveSlotNumber)), Unicode::emptyString);
else
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("The decoration layout of the structure or POB ship has been saved in slot %d (%s).", saveSlotNumber, validatedDescription.c_str())), Unicode::emptyString);
}
}
else
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
}
if ((debugNumItems > 0) && getClient()->isGod())
{
const unsigned long debugEndTimeMs = Clock::timeMs();
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("!!!GOD MODE STATISTICS!!! %d items saved in %lums", debugNumItems, (debugEndTimeMs - debugStartTimeMs))), Unicode::emptyString);
}
}
// ----------------------------------------------------------------------
void CreatureObject::restoreDecorationLayout(ServerObject const & pobTargetObject, int saveSlotNumber)
{
const unsigned long debugStartTimeMs = Clock::timeMs();
if (!isAuthoritative())
return;
if (!getClient())
return;
// only slots 1-3 are currently supported
if ((saveSlotNumber < 1) || (saveSlotNumber > 3))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
if (!pobTargetObject.isAuthoritative())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
PlayerObject * const playerObj = PlayerCreatureController::getPlayerObject(this);
if (!playerObj)
return;
ServerObject const * const containingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
if (!containingCell || !containingCell->asCellObject())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
ServerObject const * const containingPOBso = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*containingCell));
if (!containingPOBso)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
if (!containingPOBso->isInWorldCell())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
if (containingPOBso != &pobTargetObject)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be standing inside the structure or POB ship to do this."), Unicode::emptyString);
return;
}
// don't allow another restore on the pob while one is still in progress
int const timeNow = static_cast<int>(::time(NULL));
int restoreDecorationOperationTimeout = 0;
if (getObjVars().getItem("restoreDecorationOperation.timeout", restoreDecorationOperationTimeout) && (restoreDecorationOperationTimeout > timeNow))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("You are already performing a restore decoration layout operation. Please wait for the current operation to complete, or if the current operation does not complete in %s, you can try another operation.", CalendarTime::convertSecondsToMS(static_cast<unsigned int>(restoreDecorationOperationTimeout - timeNow)).c_str())), Unicode::emptyString);
return;
}
if (containingPOBso->asBuildingObject() && !containingPOBso->asBuildingObject()->getContentsLoaded())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("Please wait until the building has loaded its contents and try again."), Unicode::emptyString);
return;
}
TangibleObject const * const containingPOB = containingPOBso->asTangibleObject();
if (!containingPOB)
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
// check for lot overlimit drop restriction
if ((playerObj->getAccountNumLots() > getMaxNumberOfLots()) && getObjVars().hasItem("lotOverlimit.structure_id"))
{
NetworkId lotOverlimitStructure;
if (getObjVars().getItem("lotOverlimit.structure_id", lotOverlimitStructure) && lotOverlimitStructure.isValid())
{
if (GameObjectTypes::isTypeOf(containingPOB->getGameObjectType(), SharedObjectTemplate::GOT_building) && (containingPOB->getNetworkId() != lotOverlimitStructure))
{
std::string lotOverlimitStructureName;
std::string lotOverlimitStructureLocation;
IGNORE_RETURN(getObjVars().getItem("lotOverlimit.structure_name", lotOverlimitStructureName));
IGNORE_RETURN(getObjVars().getItem("lotOverlimit.structure_location", lotOverlimitStructureLocation));
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("You are currently over the lot limit, and cannot drop any item into (or move around items within) any factory or structure, other than the %s structure located at %s that caused you to exceed the lot limit.", lotOverlimitStructureName.c_str(), lotOverlimitStructureLocation.c_str())), Unicode::emptyString);
return;
}
}
}
if (!containingPOB->isOnAdminList(*this))
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("You must be the owner or an admin of the structure or POB ship to do this."), Unicode::emptyString);
return;
}
// build the list of rooms in the target pob
std::map<std::string, std::pair<CellObject const *, std::pair<std::pair<float, float>, std::pair<float, float> > > > pobTargetObjectRoomsToCells;
Container const * const pobContainer = ContainerInterface::getContainer(*containingPOB);
if (pobContainer)
{
for (ContainerConstIterator i = pobContainer->begin(); i != pobContainer->end(); ++i)
{
ServerObject const * const cell = safe_cast<ServerObject const *>((*i).getObject());
if (!cell || !cell->asCellObject())
continue;
CellProperty const * const cellContainer = cell->getCellProperty();
if (!cellContainer)
continue;
std::map<std::pair<uint32, uint32>, std::pair<float, float> >::const_iterator const iterFindCellHeight = s_mapCellHeightRestriction.find(std::make_pair(containingPOB->getTemplateCrc(), cellContainer->getCellNameCrc()));
if (iterFindCellHeight == s_mapCellHeightRestriction.end())
continue;
Floor const * const floor = cellContainer->getFloor();
if (!floor)
continue;
BaseExtent const * const floorExtent = floor->getExtent_l();
if (!floorExtent)
continue;
AxialBox const floorExtentBoundingBox = floorExtent->getBoundingBox();
Vector const floorExtentBoundingBoxCenter = floorExtentBoundingBox.getCenter();
pobTargetObjectRoomsToCells[cellContainer->getCellName()] = std::make_pair(cell->asCellObject(), std::make_pair(std::make_pair(iterFindCellHeight->second.first, iterFindCellHeight->second.second), std::make_pair(floorExtentBoundingBoxCenter.x, floorExtentBoundingBoxCenter.z)));
}
}
else
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
// build the list of source room to target room mapping, telling us where to move/restore the items
DynamicVariableList const & objVars = playerObj->getObjVars();
char buffer[64];
buffer[sizeof(buffer)-1] = '\0';
std::vector<Unicode::String> savedSourceRooms;
snprintf(buffer, sizeof(buffer)-1, "savedDecoration%d.rooms", saveSlotNumber);
if (!objVars.getItem(buffer, savedSourceRooms) || savedSourceRooms.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
std::vector<Unicode::String> savedTargetRooms;
snprintf(buffer, sizeof(buffer)-1, "savedDecoration%d.targetRooms", saveSlotNumber);
if (!objVars.getItem(buffer, savedTargetRooms) || savedTargetRooms.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
if (savedSourceRooms.size() != savedTargetRooms.size())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
std::map<std::string, std::string> sourceRoomToTargetRoomMapping;
for (size_t i = 0, size = savedSourceRooms.size(); i < size; ++i)
{
if (savedTargetRooms[i].empty())
continue;
std::string savedTargetRoomNarrow = Unicode::wideToNarrow(savedTargetRooms[i]);
if (pobTargetObjectRoomsToCells.count(savedTargetRoomNarrow) <= 0)
continue;
sourceRoomToTargetRoomMapping[Unicode::wideToNarrow(savedSourceRooms[i])] = savedTargetRoomNarrow;
}
if (sourceRoomToTargetRoomMapping.empty())
{
Chat::sendSystemMessage(*this, Unicode::narrowToWide("An internal error has been encountered."), Unicode::emptyString);
return;
}
int debugNumItems = 0;
int numItemsToBeMoved = 0;
unsigned int pos;
NetworkId itemId;
ServerObject const * itemObj;
ServerObject const * itemObjContainingCell;
std::string room;
Vector position, currentPosition;
Quaternion orientation;
std::map<std::string, std::string>::const_iterator iterFindSourceRoomToTargetRoomMapping;
std::map<std::string, std::pair<CellObject const *, std::pair<std::pair<float, float>, std::pair<float, float> > > >::const_iterator iterFindPobTargetObjectRoomsToCells;
std::string objvarValue;
int objvarValueIndex = 0;
std::list<std::pair<std::pair<CachedNetworkId, CachedNetworkId>, std::pair<Vector, Quaternion> > > & restoreDecorationLayoutList = playerObj->getRestoreDecorationLayoutList();
restoreDecorationLayoutList.clear();
snprintf(buffer, sizeof(buffer)-1, "savedDecoration%d.%d", saveSlotNumber, objvarValueIndex);
while (objVars.getItem(buffer, objvarValue) && !objvarValue.empty())
{
pos = 0;
while (objvarValue[pos])
{
++debugNumItems;
nextOid(objvarValue, pos, itemId);
nextString(objvarValue, pos, room);
nextFloat(objvarValue, pos, position.x);
nextFloat(objvarValue, pos, position.y);
nextFloat(objvarValue, pos, position.z);
nextFloat(objvarValue, pos, orientation.w);
nextFloat(objvarValue, pos, orientation.x);
nextFloat(objvarValue, pos, orientation.y);
nextFloat(objvarValue, pos, orientation.z);
itemObj = ServerWorld::findObjectByNetworkId(itemId);
if (!itemObj)
continue;
itemObjContainingCell = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(*itemObj));
if (!itemObjContainingCell || !itemObjContainingCell->asCellObject())
continue;
if (ContainerInterface::getContainedByObject(*itemObjContainingCell) != containingPOB)
continue;
// what destination room should this item go in?
iterFindSourceRoomToTargetRoomMapping = sourceRoomToTargetRoomMapping.find(room);
if (iterFindSourceRoomToTargetRoomMapping == sourceRoomToTargetRoomMapping.end())
{
// no destination room specified, item should not be moved
continue;
}
// find the cell in the target structure corresponding to the target room
iterFindPobTargetObjectRoomsToCells = pobTargetObjectRoomsToCells.find(iterFindSourceRoomToTargetRoomMapping->second);
if (iterFindPobTargetObjectRoomsToCells == pobTargetObjectRoomsToCells.end())
{
// target room not found, cannot move the item
continue;
}
// the height is saved as the offset from the floor
position.y += iterFindPobTargetObjectRoomsToCells->second.second.first.first;
if (position.y < iterFindPobTargetObjectRoomsToCells->second.second.first.first)
position.y = iterFindPobTargetObjectRoomsToCells->second.second.first.first;
else if (position.y > iterFindPobTargetObjectRoomsToCells->second.second.first.second)
position.y = iterFindPobTargetObjectRoomsToCells->second.second.first.second;
// x and z are saved as offset from the "center"
position.x += iterFindPobTargetObjectRoomsToCells->second.second.second.first;
position.z += iterFindPobTargetObjectRoomsToCells->second.second.second.second;
// item is not currently in the target room, need to move it
if (itemObjContainingCell->getNetworkId() != iterFindPobTargetObjectRoomsToCells->second.first->getNetworkId())
{
++numItemsToBeMoved;
restoreDecorationLayoutList.push_back(std::make_pair(std::make_pair(CachedNetworkId(*itemObj), CachedNetworkId(*iterFindPobTargetObjectRoomsToCells->second.first)), std::make_pair(position, orientation)));
continue;
}
// if the item is not currently at the desired position, need to move it
Transform const & itemCurrentTransform = itemObj->getTransform_o2p();
currentPosition = itemCurrentTransform.getPosition_p();
if (currentPosition.magnitudeBetweenSquared(position) > 0.0025) // not within 0.05m
{
++numItemsToBeMoved;
restoreDecorationLayoutList.push_back(std::make_pair(std::make_pair(CachedNetworkId(*itemObj), CachedNetworkId(*iterFindPobTargetObjectRoomsToCells->second.first)), std::make_pair(position, orientation)));
continue;
}
// if the item is not currently at the desired orientation, need to move it
Quaternion const currentOrientation(itemCurrentTransform);
if (!WithinEpsilonInclusive(currentOrientation.w, orientation.w, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.x, orientation.x, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.y, orientation.y, 0.01f) ||
!WithinEpsilonInclusive(currentOrientation.z, orientation.z, 0.01f))
{
++numItemsToBeMoved;
restoreDecorationLayoutList.push_back(std::make_pair(std::make_pair(CachedNetworkId(*itemObj), CachedNetworkId(*iterFindPobTargetObjectRoomsToCells->second.first)), std::make_pair(position, orientation)));
continue;
}
}
++objvarValueIndex;
snprintf(buffer, sizeof(buffer)-1, "savedDecoration%d.%d", saveSlotNumber, objvarValueIndex);
}
snprintf(buffer, sizeof(buffer)-1, "savedDecoration%d.description", saveSlotNumber);
std::string saveSlotDescription;
IGNORE_RETURN(objVars.getItem(buffer, saveSlotDescription));
if (restoreDecorationLayoutList.empty())
{
if (saveSlotDescription.empty())
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("No items in the structure or POB ship could be (or need to be) moved/restored using the saved decoration layout in slot %d.", saveSlotNumber)), Unicode::emptyString);
else
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("No items in the structure or POB ship could be (or need to be) moved/restored using the saved decoration layout in slot %d (%s).", saveSlotNumber, saveSlotDescription.c_str())), Unicode::emptyString);
}
else
{
if (saveSlotDescription.empty())
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("Please remain in the structure or POB ship while the system attempts to move/restore %d items using the saved decoration layout in slot %d. You will be notified when the operation is complete.", numItemsToBeMoved, saveSlotNumber)), Unicode::emptyString);
else
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<1024>().sprintf("Please remain in the structure or POB ship while the system attempts to move/restore %d items using the saved decoration layout in slot %d (%s). You will be notified when the operation is complete.", numItemsToBeMoved, saveSlotNumber, saveSlotDescription.c_str())), Unicode::emptyString);
// start messageTo loop to do the decoration layout restore,
// spread out over a period of time so it doesn't lag out the server
int restoreDecorationOperationId = ::rand();
while (restoreDecorationOperationId == 0)
restoreDecorationOperationId = ::rand();
playerObj->setRestoreDecorationOperationId(restoreDecorationOperationId);
playerObj->setRestoreDecorationPobId(containingPOB->getNetworkId());
playerObj->setRestoreDecorationTotalCount(numItemsToBeMoved);
playerObj->setRestoreDecorationAttemptedCount(0);
playerObj->setRestoreDecorationSuccessCount(0);
IGNORE_RETURN(setObjVarItem("restoreDecorationOperation.timeout", timeNow + (5 * 60)));
IGNORE_RETURN(setObjVarItem("restoreDecorationOperation.operationId", restoreDecorationOperationId));
MessageToQueue::getInstance().sendMessageToC(getNetworkId(),
"C++RestoreDecorationLayout",
"",
0,
false);
}
if ((debugNumItems > 0) && getClient()->isGod())
{
const unsigned long debugEndTimeMs = Clock::timeMs();
Chat::sendSystemMessage(*this, Unicode::narrowToWide(FormattedString<256>().sprintf("!!!GOD MODE STATISTICS!!! %d items read (%d will be moved) in %lums", debugNumItems, numItemsToBeMoved, (debugEndTimeMs - debugStartTimeMs))), Unicode::emptyString);
}
}
// ======================================================================