//======================================================================== // // 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 #include #include #include //---------------------------------------------------------------------- // 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 > 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 >::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 >::const_iterator i = s_instrumentTypeMap.find(std::string(serverTemplateName)); if (i != s_instrumentTypeMap.end()) return (*i).second.second; return 0; } // ---------------------------------------------------------------------- std::map 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 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::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 > gs_missionRequestQueue; std::vector > gs_missionRequestQueueDeferredRemoves; void removeCreatureFromMissionRequestQueue(const CreatureObject * creature) { std::vector >::iterator qiter; for(qiter = gs_missionRequestQueue.begin(); qiter != gs_missionRequestQueue.end(); ++qiter) { if((*qiter).first == creature) { gs_missionRequestQueueDeferredRemoves.push_back((*qiter)); } } } std::deque s_spawnQueue; std::deque 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::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::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 s_containerSlotStates; namespace GroupHelpers { bool creatureIsContainedInPOBShip(CreatureObject const * creatureObject); void findAllTargetsForGroup(CreatureObject * const targetObj, std::vector & targets); bool roomInGroup(GroupObject const * groupObj, int additionalMembers); GroupMemberParam const buildGroupMemberParam(CreatureObject const * creatureObject); void buildGroupMemberParamsFromCreatures(std::vector 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(atof(source.c_str()+pos)); skipField(source, pos); } // for enforcing height restriction when restoring decoration layout std::map, std::pair > 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 (getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_walk)), m_baseRunSpeed (-1.0f), m_runSpeed (safe_cast(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_run)), m_accelScale (1.0f), m_accelPercent (1.0f), m_turnScale (1.0f), m_slopeModAngle (safe_cast(getSharedTemplate())->getSlopeModAngle() * PI / 180), m_baseSlopeModPercent (safe_cast(getSharedTemplate())->getSlopeModPercent()), m_slopeModPercent (1.0f), m_waterModPercent (safe_cast(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(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(getSharedTemplate()); NOT_NULL(sharedTemplate); // Handle scale. // setScaleFactor(safe_cast(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( 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(ConfigServerGame::getDefaultHealthRegen())); m_attributes.set(Attributes::Stamina, static_cast(ConfigServerGame::getDefaultActionRegen())); m_attributes.set(Attributes::Willpower, static_cast(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(i); m_maxMentalStates.set(i, static_cast(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 (getSharedTemplate ())->getSpeedMin (SharedCreatureObjectTemplate::MT_walk), safe_cast (getSharedTemplate ())->getSpeed (SharedCreatureObjectTemplate::MT_run), safe_cast (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_walk), safe_cast (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_run), safe_cast (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_walk), safe_cast (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(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::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::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( 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(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::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(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::iterator i = s_spawnQueue.begin(); i != s_spawnQueue.end(); ++i) { ServerObject const * const obj = safe_cast(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((*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 CreatureObject::getMissions() { std::vector 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((*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(target.getObjectTemplate()); NOT_NULL(targetTemplate); bool canMoveInWorld = false; bool godCanMove = false; ServerObject const * const serverTarget = safe_cast(&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(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(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(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(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(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(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(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(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(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(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 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 " 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 " collection slot and clear all the other "born on " collection slots; // this still needs to be run even if collectionSlot is NULL in order to forcefully clear all of the // other "born on " 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 const & slots = CollectionsDataTable::getSlotsInCollection("born_on_collection"); for (std::vector::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(ConfigServerGame::getDefaultHealthRegen())); m_attributes.set(Attributes::Stamina, static_cast(ConfigServerGame::getDefaultActionRegen())); m_attributes.set(Attributes::Willpower, static_cast(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(getSharedTemplate())->getSpeed(SharedCreatureObjectTemplate::MT_walk))); setBaseRunSpeed((safe_cast(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 > skillModBonuses; std::vector > 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((*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 const &bonus = skillModBonuses[j]; addModBonus(bonus.first, bonus.second); } } equippedItem->getAttribBonuses(attribBonuses); bonusCount = attribBonuses.size(); { for (int j = 0; j < bonusCount; ++j) { std::pair 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::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 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(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(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 >::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 > const &statMods = skill->getStatisticModifiers(); std::vector >::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(getController()); } //---------------------------------------------------------------------- const CreatureController* CreatureObject::getCreatureController() const { return safe_cast(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(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(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 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::iterator i = found.begin(); i != found.end(); ++i) { if ((*i)->getNetworkId() != getNetworkId()) safe_cast(*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(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(ObjectTemplateList::fetch(ConfigServerGame::getFallbackDefaultWeapon())); FATAL(weaponTemplate == NULL, ("CreatureObject::initializeFirstTimeObject fallback weapon template %s is bad!", ConfigServerGame::getFallbackDefaultWeapon())); } WeaponObject * const weapon = safe_cast(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(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(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( value + m_cachedCurrentAttributeModValues[attribute]); // modify for partial regeneration value = static_cast(value + static_cast(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( 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(ConfigServerGame::getMinPoolValue()); // double-check that the max isn't <= 0 if (value < 1) value = 1; } return value; } //----------------------------------------------------------------------- const std::map > & CreatureObject::getModMap() const { return m_modMap.getMap(); } //----------------------------------------------------------------------- const int CreatureObject::getModValue(const std::string & modName) const { int result = 0; std::map >::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 >::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 >::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 >::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::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 >::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::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( 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 *msg = new MessageQueueGenericValueType(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(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(m_currentAttitude.get().m_currentValues[attribute]); CreatureAttitude const * const cs = getAttitudeToward(target); if (cs) { value = static_cast(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 & CreatureObject::getExperiencePoints() const { static const std::map 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 > & statMods = newSkill.getStatisticModifiers(); std::vector >::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(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 > & statMods = oldSkill.getStatisticModifiers(); std::vector >::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(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(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(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::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 = ""; 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(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 nameCrcs; AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs); // if we have any name that matches a value in nameCrcs, we can return true for (std::vector::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 nameCrcs; AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs); // if we have any name that matches a value in nameCrcs, we can return true std::map::const_iterator f; for (std::vector::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::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(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::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::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 nameCrcs; AttribModNameManager::getInstance().getAttribModCrcsFromBase(modName, nameCrcs); // if we have any name that matches a value in nameCrcs, we can return it for (std::vector::const_iterator i = nameCrcs.begin(); i != nameCrcs.end(); ++i) { std::map::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 & 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 > * const msg = new MessageQueueGenericValueType >( std::make_pair(id, time)); getController()->appendMessage( static_cast(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 * const msg = new MessageQueueGenericValueType(id); getController()->appendMessage( static_cast(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(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::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 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::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::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(newCachedMaxAttributeModValues[i])); m_cachedCurrentAttributeModValues.set(i, static_cast(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( m_cachedMaxAttributeModValues[i] - healedAttrib + 1)); m_cachedCurrentAttributeModValues.set(i, static_cast( 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::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(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(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(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::const_iterator targetIterator, targetEnd = m_mentalStatesToward.end(); for (targetIterator=m_mentalStatesToward.begin(); targetIterator != targetEnd; ++targetIterator) { newTargetState = targetIterator->second; for (i=0; isecond.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::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(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 const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData(); std::map::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 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::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 > > *msg = new MessageQueueGenericValueType > >(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 targetListChanged; targetListChanged.reserve(Behaviors::NumberOfBehaviors); targetListChanged.resize(Behaviors::NumberOfBehaviors); int i; for (i=0; itrigAllScripts(notification, params)); } } // ---------------------------------------------------------------------- CreatureAttitude const *CreatureObject::getAttitudeToward(NetworkId const &target) const { Archive::AutoDeltaMap::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::const_iterator i = m_mentalStatesToward.begin(); Archive::AutoDeltaMap::const_iterator e = m_mentalStatesToward.end(); for (; i != e; ++i) { CreatureAttitude newState = i->second; for (state=0; statesecond.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::const_iterator i = m_mentalStatesToward.begin(); Archive::AutoDeltaMap::const_iterator e = m_mentalStatesToward.end(); std::vector to_erase; for (; i != e; ++i) { ServerObject * object = ServerWorld::findObjectByNetworkId(i->first); bool erase = true; int state; if (object) { for (state=0; statesecond.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::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::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::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::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(state.m_fearState - 1); } while ((state.m_fearState < CreatureAttitude::kFearStateCount-1) && fear >= m_fearCutoffs[state.m_fearState+1]) { state.m_fearState = static_cast(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(state.m_angerState - 1); } while ((state.m_angerState < CreatureAttitude::kAngerStateCount-1) && anger >= m_angerCutoffs[state.m_angerState+1]) { state.m_angerState = static_cast(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,int> & CreatureObject::getDraftSchematics() const { static const std::map,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 & 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((*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 & 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((*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 &targets) { if (behavior < 0 || behavior >= Behaviors::NumberOfBehaviors) { return; } targets.clear(); targets.reserve(m_targetsByBehavior[behavior].size()); targets.resize(m_targetsByBehavior[behavior].size()); std::set::const_iterator i; std::vector::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(getSharedTemplate()); return static_cast(sharedTemplate->getNiche()); } //---------------------------------------------------------------------- SharedCreatureObjectTemplate::Species CreatureObject::getSpecies() const { const SharedCreatureObjectTemplate * sharedTemplate = safe_cast(getSharedTemplate()); return static_cast(sharedTemplate->getSpecies()); } //---------------------------------------------------------------------- SharedCreatureObjectTemplate::Race CreatureObject::getRace() const { const SharedCreatureObjectTemplate * sharedTemplate = safe_cast(getSharedTemplate()); return static_cast(sharedTemplate->getRace()); } //---------------------------------------------------------------------- /* void CreatureObject::setAnimationState(CreatureObject::AnimationState state) { m_animState = static_cast(state); } */ //---------------------------------------------------------------------- void CreatureObject::setMood(uint32 mood) { if(isAuthoritative()) { DEBUG_WARNING (mood > 255, ("Mood %d out of range", mood)); m_mood = static_cast(mood); } else { sendControllerMessageToAuthServer(CM_setMood, new MessageQueueGenericValueType(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( 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::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 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(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(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(whichState), value)); } } //---------------------------------------------------------------------- void CreatureObject::setSayMode(uint32 sayMode) { if(isAuthoritative()) { DEBUG_WARNING (sayMode > 255, ("Saymode %d out of range", sayMode)); m_sayMode = static_cast(sayMode); } else { sendControllerMessageToAuthServer(CM_setSayMode, new MessageQueueGenericValueType(sayMode)); } } //---------------------------------------------------------------------- void CreatureObject::setAnimationMood(const std::string & mood) { if(isAuthoritative()) { m_animationMood = mood; } else { sendControllerMessageToAuthServer(CM_setAnimationMood, new MessageQueueGenericValueType(mood)); } } //---------------------------------------------------------------------- void CreatureObject::setAttribute(Attributes::Enumerator attribute, Attributes::Value value) { if(!isAuthoritative()) { sendControllerMessageToAuthServer(CM_setAttribute, new MessageQueueGenericValueType >(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::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::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::make_pair(state, value))); } } //---------------------------------------------------------------------- void CreatureObject::setMentalStateDecay(MentalStates::Enumerator state, float value) { if(! isAuthoritative()) { sendControllerMessageToAuthServer(CM_setMentalStateDecay, new MessageQueueGenericValueType >(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(s->getObjectInSlot(dp, tmp).getObject()); } return 0; } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getObjectInSlot (const ConstCharCrcLowerString & slotName) const { return const_cast(this)->getObjectInSlot (slotName); } //----------------------------------------------------------------------- ServerObject * CreatureObject::getDatapad() { return getObjectInSlot (SlotNames::datapad); } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getDatapad() const { return const_cast(this)->getDatapad (); } //---------------------------------------------------------------------- ServerObject * CreatureObject::getBank () { return getObjectInSlot (SlotNames::bank); } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getBank () const { return const_cast(this)->getBank (); } //----------------------------------------------------------------------- ServerObject * CreatureObject::getInventory() { return getObjectInSlot (SlotNames::inventory); } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getInventory() const { return const_cast(this)->getInventory (); } //---------------------------------------------------------------------- ServerObject * CreatureObject::getHair() { return getObjectInSlot (SlotNames::hair); } //------------------------------------------------------------------------ const ServerObject * CreatureObject::getHair() const { return const_cast(this)->getHair (); } //---------------------------------------------------------------------- ServerObject * CreatureObject::getAppearanceInventory() { return getObjectInSlot (SlotNames::appearance); } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getAppearanceInventory() const { return const_cast(this)->getAppearanceInventory (); } //---------------------------------------------------------------------- ServerObject * CreatureObject::getHangar() { return getObjectInSlot (SlotNames::hangar); } //---------------------------------------------------------------------- const ServerObject * CreatureObject::getHangar() const { return const_cast(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 > skillModBonuses; tangibleObject->getSkillModBonuses(skillModBonuses); int const bonusCount = skillModBonuses.size(); if (bonusCount > 0) { std::vector modNames(bonusCount); std::vector modValues(bonusCount); for (int i = 0; i < bonusCount; ++i) { std::pair 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 > attribBonuses; tangibleObject->getAttribBonuses(attribBonuses); int const bonusCount = attribBonuses.size(); for (int i = 0; i < bonusCount; ++i) { std::pair 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 > skillModBonuses; tangibleObject->getSkillModBonuses(skillModBonuses); int const bonusCount = skillModBonuses.size(); if (bonusCount > 0) { std::vector modNames(bonusCount); std::vector modValues(bonusCount); for (int i = 0; i < bonusCount; ++i) { std::pair 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 > attribBonuses; tangibleObject->getAttribBonuses(attribBonuses); int const bonusCount = attribBonuses.size(); for (int i = 0; i < bonusCount; ++i) { std::pair 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(ContainerInterface::getTopmostContainer(*oldContainer)) : this; ServerObject * const newTopmost = newContainer ? safe_cast(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(floor(m_regeneration[ poolAttrib])); if (delta >= 1.0f) { m_regeneration[poolAttrib] -= delta; IGNORE_RETURN(alterAttribute(poolAttrib, static_cast( 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(getUnmodifiedAttribute(i)), static_cast(getUnmodifiedMaxAttribute(i)), static_cast(m_cachedCurrentAttributeModValues[i]), static_cast(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(getAttribute(i)), static_cast(getUnmodifiedAttribute(i)), static_cast(getMaxAttribute(i)), static_cast(getUnmodifiedMaxAttribute(i)), static_cast(m_cachedCurrentAttributeModValues[i]), static_cast(m_cachedMaxAttributeModValues[i]))); } } } } for (i = 0; i < Attributes::NumberOfAttributes; ++i) { if (Attributes::isAttribPool(i)) { m_totalAttributes.set(i, getAttribute(i) - static_cast(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(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 expiredMods; std::vector::const_iterator iterTimedMod = m_timedMod.begin(); std::vector::const_iterator iterTimedModDuration = m_timedModDuration.begin(); std::vector::const_iterator iterTimedModUpdateTime = m_timedModUpdateTime.begin(); for (; iterTimedMod != m_timedMod.end(); ++iterTimedMod, ++iterTimedModDuration, ++iterTimedModUpdateTime) { if (*iterTimedModDuration >= 0) { if ((*iterTimedModUpdateTime + static_cast(*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(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 > 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 >::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 >::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(ServerClock::getInstance().getGameTimeSeconds()); for (std::map >::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(currentGameTime - iterCooldown->second.first)); msg->setMaxTime (MessageQueueCommandTimer::F_cooldown, static_cast(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 > const & cooldowns = commandQueue->getCooldowns().getMap(); for (std::map >::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(currentTime - cooldownsIter->second.first); maxCooldownTimeSecond = static_cast(cooldownsIter->second.second - cooldownsIter->second.first); if (currentCooldownTimeSecond < maxCooldownTimeSecond) { GenericValueTypeMessage > > 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(player->getSecondsUntilUnsquelched()); if (timeUnsquelch > 0) timeUnsquelch += ::time(NULL); GenericValueTypeMessage, int>, std::pair > > chatStatistics("ChatStatisticsGS", std::make_pair(std::make_pair(std::make_pair(getNetworkId(), static_cast(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(getCreatureController()); if(playerController) { GenericValueTypeMessage 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::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::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 toRemove; Archive::AutoDeltaMap::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::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 ¶ms, 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(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(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::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 >(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(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(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(wound)); } } //---------------------------------------------------------------------- void CreatureObject::setLookAtTarget(const NetworkId& id) { if(isAuthoritative()) { m_lookAtTarget.set(id); } else { sendControllerMessageToAuthServer(CM_setLookAtTarget, new MessageQueueGenericValueType(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(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(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( 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(percent)); } return true; } //-------------------------------------------------------------------- bool CreatureObject::setWaterModPercent(float percent) { if(isAuthoritative()) { m_waterModPercent = percent; } else { sendControllerMessageToAuthServer(CM_setWaterModPercent, new MessageQueueGenericValueType(percent)); } return true; } //-------------------------------------------------------------------- bool CreatureObject::setMovementScale(float scale) { if(isAuthoritative()) { m_movementScale = scale; } else { sendControllerMessageToAuthServer(CM_setMovementScale, new MessageQueueGenericValueType(scale)); } return true; } // ---------------------------------------------------------------------- bool CreatureObject::setMovementPercent(float percent) { if(isAuthoritative()) { m_movementPercent = percent; } else { sendControllerMessageToAuthServer(CM_setMovementPercent, new MessageQueueGenericValueType(percent)); } return true; } //-------------------------------------------------------------------- bool CreatureObject::setTurnPercent(float percent) { if(isAuthoritative()) { m_turnScale = percent; } else { sendControllerMessageToAuthServer(CM_setTurnPercent, new MessageQueueGenericValueType(percent)); } return true; } //-------------------------------------------------------------------- bool CreatureObject::setAccelScale(float scale) { if(isAuthoritative()) { m_accelScale = scale; } else { sendControllerMessageToAuthServer(CM_setAccelScale, new MessageQueueGenericValueType(scale)); } return true; } // ---------------------------------------------------------------------- bool CreatureObject::setAccelPercent(float percent) { if(isAuthoritative()) { m_accelPercent = percent; } else { sendControllerMessageToAuthServer(CM_setAccelPercent, new MessageQueueGenericValueType(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(controller); ServerController * const serverController = safe_cast(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 Payload; MessageQueueGenericValueType * const msg = new MessageQueueGenericValueType(Payload(owner, bio)); creatureController->appendMessage(static_cast(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 Payload; MessageQueueGenericValueType * const msg = new MessageQueueGenericValueType(results); creatureController->appendMessage(static_cast(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(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 const &skillsProviding = SpatialChatManager::getSkillsProviding(spatialChat.getChatType()); if (!skillsProviding.empty()) { bool found = false; for (std::vector::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 *msg = new MessageQueueGenericValueType(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 *msg = new MessageQueueGenericValueType(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 *msg = new MessageQueueGenericValueType(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 *msg = new MessageQueueGenericValueType(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(masterId)); } // ---------------------------------------------------------------------- float CreatureObject::getSwimHeight() const { return safe_cast(getSharedTemplate())->getSwimHeight() * getScaleFactor(); } //----------------------------------------------------------------------- const CreatureObject::AllCreaturesSet & CreatureObject::getAllCreatures() { return g_creatureList; } //----------------------------------------------------------------------- void CreatureObject::addToMissionRequestQueue(const NetworkId & terminalId) { if (!ConfigServerGame::getDisableMissions()) { std::vector >::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(NetworkIdManager::getObjectById(creatureId)); if (creature) { removeCreatureFromMissionRequestQueue(creature); } } //----------------------------------------------------------------------- void CreatureObject::updateMissionRequestQueue() { if(! gs_missionRequestQueue.empty()) { // remove pending creatures from previous run std::vector >::const_iterator remIter; std::vector >::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 missionObjects; Container * container = ContainerInterface::getContainer(*missionBag); ContainerIterator iter; for(iter = container->begin(); iter != container->end(); ++iter) { MissionObject * missionObject = safe_cast((*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 >::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(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(position.x), static_cast(position.y), static_cast(position.z), effectiveInterestRadius, static_cast(getLocationReservationRadius()), false, true, static_cast(getObjectType()), static_cast(getLevel()), hibernating, getTemplateCrc(), aiCreatureController ? aiCreatureController->getMovementType() : -1, getIsStatic() == true ? 1 : 0); GameServer::getInstance().sendToPlanetServer(msg); } } } // ---------------------------------------------------------------------- int CreatureObject::getCurrentTargetsTotal(const std::map & targets) { int sum = 0; for(std::map::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(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(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(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 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(::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 > > > const msg("PlayerRenameRequestSubmitted", std::make_pair(static_cast(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 const & packedData = message.getPackedDataVector(); std::vector::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(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 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(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 const & cityId = CityInterface::getCitizenOfCityId(getNetworkId()); if (!cityId.empty()) { std::string cityName; for (std::vector::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 > 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(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(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(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(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(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(NetworkIdManager::getObjectById(group)) : NULL); groupObject = (so ? so->asGroupObject() : NULL); } if (success) { std::vector 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(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(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(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 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::const_iterator ii = targets.begin(); std::vector::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(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::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::const_iterator ii = targets.begin(); std::vector::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(commandHash)); if (!command.isNull()) { commandQueueEnqueue(command, NetworkId::cms_invalid, Unicode::String(), 0, false, static_cast(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(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(x), static_cast(y), static_cast(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(::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 > 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(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(::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 > 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(response))); } } } else if (message.getMethod() == "C++SwapUnlockedSlotRsp") { if (!message.getPackedDataVector().empty()) { Archive::ByteStream bs; bs.put(reinterpret_cast(&message.getPackedDataVector()[0]), static_cast(message.getPackedDataVector().size())); Archive::ReadIterator ri = bs.begin(); GenericValueTypeMessage, std::pair > > > const swapUnlockedSlotRsp(ri); LoginUpgradeAccountMessage::SwapUnlockedSlotResponse const response = static_cast(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 > const characterChangedNormal("CharacterChangedNormal", std::make_pair(GameServer::getInstance().getClusterName(), getNetworkId())); client->send(characterChangedNormal, true); GenericValueTypeMessage > 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(&message.getPackedDataVector()[0]), static_cast(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 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::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 const & featureIds = msg.getFeatureIds(); ConsoleMgr::broadcastString(FormattedString<1024>().sprintf("(%d) feature Ids for account (%lu)", featureIds.size(), msg.getTargetStationId()), getClient()); for (std::map::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(), 0, false); } } } else if (message.getMethod() == "C++FeatureIdTransactionResponse") { if (!message.getPackedDataVector().empty()) { Archive::ByteStream bs; bs.put(reinterpret_cast(&message.getPackedDataVector()[0]), static_cast(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(&message.getPackedDataVector()[0]), static_cast(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(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(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(result))); } } } } else if (message.getMethod() == "C++TransferReplyNameValidation") { if (!message.getPackedDataVector().empty()) { Archive::ByteStream bs; bs.put(reinterpret_cast(&message.getPackedDataVector()[0]), static_cast(message.getPackedDataVector().size())); Archive::ReadIterator ri = bs.begin(); GenericValueTypeMessage > 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(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(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 & processedItemsSoFar = playerObject->getPickupDropAllItemsProcessedItemsSoFar(); std::list 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::const_iterator iter = cellContents.begin(); iter != cellContents.end(); ++iter) { --numberOfItemsToProcessThisRound; processedItemsSoFar.insert(*iter); cellContent = safe_cast(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(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(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 & processedItemsSoFar = playerObject->getPickupDropAllItemsProcessedItemsSoFar(); std::list 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::const_iterator iter = inventoryContents.begin(); iter != inventoryContents.end(); ++iter) { --numberOfItemsToProcessThisRound; processedItemsSoFar.insert(*iter); inventoryContent = safe_cast(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(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(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 > > & restoreDecorationLayoutList = playerObject->getRestoreDecorationLayoutList(); while (!restoreDecorationLayoutList.empty() && (numberOfItemsToProcessThisRound > 0)) { std::pair, std::pair > 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(getSharedTemplate())->getStepHeight(); } //---------------------------------------------------------------------- float CreatureObject::getApproachTriggerRange() const { return static_cast(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(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(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(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(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(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(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(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 CreatureObject::uploadCharacterData(const bool withItems, const bool allowOverride) { std::vector result; if(getClient()) { GameScriptObject * scriptObject = getScriptObject(); if(scriptObject) { int resultSize = 0; std::vector 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(i)]); } LOG("CustomerService", ("CharacterTransfer: CreatureObject::uploadCharacterData() yields a buffer %d bytes long", result.size())); } } return result; } // ---------------------------------------------------------------------- bool CreatureObject::receiveCharacterTransferMessage(const std::vector & 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 > > 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 const & CreatureObject::getJediBountiesOnMe() const { static const std::vector 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((*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(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(GameServer::getInstance().getProcessId()), sharedObjectTemplateName.c_str() )); return; } ObjectTemplate const *sharedObjectTemplate = ObjectTemplateList::fetch(sharedObjectTemplateName); SharedCreatureObjectTemplate const *const sharedObjectTemplateAsCreature = dynamic_cast(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 (getSharedTemplate ())->getAccelerationMax (SharedCreatureObjectTemplate::MT_walk); else accel = safe_cast (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 (getSharedTemplate ())->getTurnRate (SharedCreatureObjectTemplate::MT_walk); else turnRate = safe_cast (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(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(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(GameServer::getInstance().getProcessId()), static_cast(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(GameServer::getInstance().getProcessId()) )); return; } //-- Collect items to move into inventory. This includes items in all slots that players can manipulate. typedef std::vector 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(errorCode) )); } } } // ---------------------------------------------------------------------- void CreatureObject::virtualOnSetClient() { TangibleObject::virtualOnSetClient(); handleTutorialTransition(); setNumberOfMissionsWantedInMissionBag(ConfigServerGame::getNumberOfMissionsWantedInMissionBag()); } //---------------------------------------------------------------------- std::vector CreatureObject::getWearableData() { return m_wearableData.get(); } //---------------------------------------------------------------------- void CreatureObject::setWearableData(std::vector 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 wearableData = getWearableData(); for(std::vector::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 > & 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(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 >::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(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 newRegions; newRegions.clear(); RegionMaster::getRegionsAtPoint(ServerWorld::getSceneId(), getPosition_w().x, getPosition_w().z, newRegions); if(forceCheckRegions) { m_regionFlagTimer.reset(); std::vector::iterator regionIter = newRegions.begin(); EnvironmentInfo flags = static_cast(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 newNotifyRegions; newNotifyRegions.clear(); static std::vector newNotifyRegionsEntered; newNotifyRegionsEntered.clear(); static std::vector > notifyRegionsExited; notifyRegionsExited.clear(); // look for region enter events std::vector::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(commandHash)); if (!command.isNull()) { commandQueueEnqueue(command, mountedCreature->getNetworkId(), mountedCreature->getObjectName(), 0, false, static_cast(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 >::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 >::const_iterator i3; for(i3 = notifyRegionsExited.begin(); i3 != notifyRegionsExited.end(); ++i3) { m_notifyRegions.erase(*i3); } std::vector::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 requiredCerts; item.getRequiredCertifications(requiredCerts); for (std::vector::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(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(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(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(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(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::make_pair(static_cast(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(defaultDuration); totalDuration = defaultDuration; } } else { timestamp += static_cast(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::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(BuffManager::getBuffMaxStacks(buffNameCrc)))); Buff::PackedBuff packedValue = Buff(buffNameCrc, timestamp, dynamicValue, static_cast(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(buffState), true); } // ---------------------------------------------------------------------- void CreatureObject::removeBuff(uint32 buffNameCrc) { if (!isAuthoritative()) { sendControllerMessageToAuthServer(CM_removeBuff, new MessageQueueGenericValueType(static_cast(buffNameCrc))); return; } std::map::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(buffState), false); std::string const &callback = BuffManager::getBuffCallback(buffNameCrc); ScriptParams params; params.addParam(static_cast(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_iterator i = m_buffs.find(buffNameCrc); if (i != m_buffs.end()) { Buff::PackedBuff b = (*i).second; b.stackCount -= stacksToRemove; removeBuff(buffNameCrc); if (static_cast(b.stackCount) > 0) { uint32 currentTime = internalGetPlayedTime(); if (BuffManager::getBuffIsCelestial(buffNameCrc)) { currentTime = ServerClock::getInstance().getGameTimeSeconds(); } float endTime = static_cast (b.endtime) - static_cast (currentTime); addBuff( buffNameCrc, endTime , b.value, static_cast (b.duration), b.caster, b.stackCount); } } } // ---------------------------------------------------------------------- void CreatureObject::decayBuff(uint32 buffNameCrc, float decayPercentage) { std::map::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 (b.endtime) - decayValue) - static_cast (currentTime); if(endTime > 0.0f) addBuff( buffNameCrc, endTime , b.value, static_cast (b.duration), b.caster, b.stackCount); } } uint32 CreatureObject::internalGetPlayedTime() const { if (isPlayerControlled()) { PlayerObject const * const playerObject = PlayerCreatureController::getPlayerObject(this); if (playerObject) { return static_cast(playerObject->getPlayedTime()); } } return static_cast(ServerClock::getInstance().getGameTimeSeconds()); } // ---------------------------------------------------------------------- // This functions expires buffs as needed void CreatureObject::updateBuffs() { uint32 timestamp = internalGetPlayedTime(); std::list< Buff > buffsToRemove; for (std::map::const_iterator i = m_buffs.begin(); i != m_buffs.end(); ) { const std::pair & 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::const_iterator i = m_buffs.begin(); i != m_buffs.end(); ++i) { const std::pair & p = *i; if ((p.first != buffNameCrc) && (BuffManager::getBuffState(p.first) == state)) return true; } return false; } // ---------------------------------------------------------------------- void CreatureObject::getAllBuffs(stdvector::fwd & buffCrcs) const { buffCrcs.clear(); buffCrcs.reserve(m_buffs.size()); for (std::map::const_iterator i = m_buffs.begin(); i != m_buffs.end(); ++i) { const std::pair & p = *i; buffCrcs.push_back(p.first); } } // ---------------------------------------------------------------------- bool CreatureObject::getBuff(uint32 buffNameCrc, Buff & buff) const { std::map::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::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(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 const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData(); std::map::const_iterator iterFind = connectedCharacterLfgData.find(getNetworkId()); if ((iterFind != connectedCharacterLfgData.end()) && (iterFind->second.level != static_cast(level))) ServerUniverse::setConnectedCharacterLevelData(getNetworkId(), static_cast(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 tempPersistedHolder; for (std::map::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::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(b.m_timestamp - currentTime); addBuff(b.m_nameCrc, timeTillRemoval, b.m_value, static_cast(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 const & experienceMap = playerObject->getExperiencePoints(); std::map::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(m_difficulty.get()); } // ---------------------------------------------------------------------- void CreatureObject::setDifficulty(Difficulty const difficulty) { m_difficulty = static_cast(difficulty); } // ---------------------------------------------------------------------- void CreatureObject::setHologramType(int32 const newHologramType) { if (isAuthoritative()) { m_hologramType = newHologramType; } else { sendControllerMessageToAuthServer(CM_modifyHologramType, new MessageQueueGenericValueType(newHologramType)); } } // ---------------------------------------------------------------------- void CreatureObject::setVisibleOnMapAndRadar(bool const newVal) { if (isAuthoritative()) { m_visibleOnMapAndRadar = newVal; } else { sendControllerMessageToAuthServer(CM_modifyVisibleOnMapAndRadar, new MessageQueueGenericValueType(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(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(floor(value))); } else { sendControllerMessageToAuthServer(CM_setRegenRate, new MessageQueueGenericValueType >(std::make_pair(static_cast(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(getController()); if(playerCreatureController) return (playerCreatureController->getLastSpeed() > ConfigSharedGame::getMovementThresholdForActionValidation()); return true; } // ---------------------------------------------------------------------- CommandQueue * CreatureObject::getCommandQueue() const { if (m_commandQueue == NULL) { m_commandQueue = CommandQueue::getCommandQueue(*const_cast(this)); } return m_commandQueue; } // ---------------------------------------------------------------------- void CreatureObject::clearCommands() { m_commands.clear(); } // ---------------------------------------------------------------------- std::map 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::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(commandName)); } return result; } //----------------------------------------------------------------------- void CreatureObject::revokeCommand(std::string const & command, bool const fromSkill, bool const ignoreCount) { if(isAuthoritative()) { Archive::AutoDeltaMap::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(command)); } } //----------------------------------------------------------------------- bool CreatureObject::hasCommand(std::string const & commandName) const { bool result = false; if (commandName == "unskilled") result = true; else { const Archive::AutoDeltaMap::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 const &addExpertisesNamesList, bool clearAllExpertisesFirst) { for(std::vector::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 skillsToRevoke; for(std::set::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::iterator i2 = skillsToRevoke.begin(); i2 != skillsToRevoke.end(); ++i2) { const SkillObject *skill = *i2; revokeSkill(*skill, true); } numTries++; } return false; } //----------------------------------------------------------------------- void CreatureObject::recomputeCommandSeries() { for(std::vector::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 const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData(); std::map::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(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 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(ContainerInterface::getContainedByObject(*this)); while (o) { if (o->asCellObject()) { inCell = true; break; } o = safe_cast(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(creaturePosition.x), -8000), 7999); // clamp to -8000 - 7999 int posZ = std::min(std::max(static_cast(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(creaturePosition.x), static_cast(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 & targets) { typedef std::vector 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 const & targets, GroupObject::GroupMemberParamVector & targetMemberParams) { std::vector::const_iterator ii = targets.begin(); std::vector::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(item.getObject()); if (!itemSo) return; // make sure item is immediately in a cell/room ServerObject const * const itemContainingCell = safe_cast(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(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 * msg = new MessageQueueGenericValueType(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 * msg = new MessageQueueGenericValueType(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(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(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(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(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 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((*i).getObject()); if (cell && cell->asCellObject()) { CellProperty const * const cellContainer = cell->getCellProperty(); if (cellContainer) { std::map, std::pair >::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((*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(::time(NULL))); snprintf(buffer1, sizeof(buffer1)-1, "savedDecoration%d.pobName", saveSlotNumber); playerObj->setObjVarItem(buffer1, containingPOB->getObjectNameStringId().localize()); std::vector roomsVector; for (std::set::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(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(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(::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(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::pair > > > 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((*i).getObject()); if (!cell || !cell->asCellObject()) continue; CellProperty const * const cellContainer = cell->getCellProperty(); if (!cellContainer) continue; std::map, std::pair >::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 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 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 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::const_iterator iterFindSourceRoomToTargetRoomMapping; std::map, std::pair > > >::const_iterator iterFindPobTargetObjectRoomsToCells; std::string objvarValue; int objvarValueIndex = 0; std::list, std::pair > > & 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(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); } } // ======================================================================