// ====================================================================== // // TangibleObject.cpp // Copyright 2001, 2002 Sony Online Entertainment Inc. // All Rights Reserved. // // ====================================================================== #include "serverGame/FirstServerGame.h" #include "serverGame/TangibleObject.h" #include "SwgGameServer/CombatEngine.h" #include "SwgGameServer/ConfigCombatEngine.h" #include "serverGame/AiCombatPulseQueue.h" #include "serverGame/AiCreatureController.h" #include "serverGame/Chat.h" #include "serverGame/CityInfo.h" #include "serverGame/CityInterface.h" #include "serverGame/Client.h" #include "serverGame/CombatTracker.h" #include "serverGame/CommandQueue.h" #include "serverGame/ConfigServerGame.h" #include "serverGame/ContainerInterface.h" #include "serverGame/CreatureObject.h" #include "serverGame/DraftSchematicObject.h" #include "serverGame/GameServer.h" #include "serverGame/GroupObject.h" #include "serverGame/ManufactureSchematicObject.h" #include "serverGame/MessageToQueue.h" #include "serverGame/NameManager.h" #include "serverGame/NetworkTriggerVolume.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/Pvp.h" #include "serverGame/RegionMaster.h" #include "serverGame/RegionPvp.h" #include "serverGame/ResourceContainerObject.h" #include "serverGame/ResourceTypeObject.h" #include "serverGame/ServerCollisionProperty.h" #include "serverGame/ServerLotManagerNotification.h" #include "serverGame/ServerMessageForwarding.h" #include "serverGame/ServerResourceClassObject.h" #include "serverGame/ServerSynchronizedUi.h" #include "serverGame/ServerTangibleObjectTemplate.h" #include "serverGame/ServerUniverse.h" #include "serverGame/ServerWorld.h" #include "serverGame/ShipComponentDataManager.h" #include "serverGame/ShipObject.h" #include "serverGame/TangibleController.h" #include "serverNetworkMessages/MessageQueueCommandQueueEnqueueFwd.h" #include "serverNetworkMessages/MessageQueueCommandQueueForceExecuteCommandFwd.h" #include "serverNetworkMessages/MessageToPayload.h" #include "serverNetworkMessages/UpdateObjectOnPlanetMessage.h" #include "serverPathfinding/ServerPathfindingNotification.h" #include "serverScript/GameScriptObject.h" #include "serverScript/ScriptDictionary.h" #include "serverScript/ScriptFunctionTable.h" #include "serverScript/ScriptParameters.h" #include "serverUtility/ServerClock.h" #include "sharedDebug/Profiler.h" #include "sharedFile/FileNameUtils.h" #include "sharedFoundation/ConstCharCrcLowerString.h" #include "sharedFoundation/ConstCharCrcString.h" #include "sharedFoundation/DynamicVariable.h" #include "sharedFoundation/DynamicVariableList.h" #include "sharedFoundation/GameControllerMessage.h" #include "sharedFoundation/Os.h" #include "sharedGame/Command.h" #include "sharedGame/CraftingData.h" #include "sharedGame/GameObjectTypes.h" #include "sharedGame/LfgCharacterData.h" #include "sharedGame/PvpData.h" #include "sharedGame/SharedObjectAttributes.h" #include "sharedGame/SharedObjectTemplate.h" #include "sharedGame/SharedStringIds.h" #include "sharedGame/SharedTangibleObjectTemplate.h" #include "sharedGame/ShipComponentData.h" #include "sharedLog/Log.h" #include "sharedMath/PackedArgb.h" #include "sharedNetworkMessages/GenericValueTypeMessage.h" #include "sharedNetworkMessages/IncubatorCommitMessage.h" #include "sharedNetworkMessages/MessageQueueCommandQueueRemove.h" #include "sharedNetworkMessages/MessageQueueCraftIngredients.h" #include "sharedNetworkMessages/MessageQueueGenericValueType.h" #include "sharedNetworkMessages/MessageQueueNetworkId.h" #include "sharedNetworkMessages/MessageQueueOpponentInfo.h" #include "sharedNetworkMessages/MessageQueueShowFlyText.h" #include "sharedNetworkMessages/MessageQueueShowCombatText.h" #include "sharedNetworkMessages/MessageQueueStartNpcConversation.h" #include "sharedNetworkMessages/MessageQueueStopNpcConversation.h" #include "sharedNetworkMessages/ObjectChannelMessages.h" #include "sharedNetworkMessages/UpdatePvpStatusMessage.h" #include "sharedObject/AlterResult.h" #include "sharedObject/AppearanceTemplateList.h" #include "sharedObject/ContainedByProperty.h" #include "sharedObject/Container.h" #include "sharedObject/CustomizationData.h" #include "sharedObject/CustomizationDataProperty.h" #include "sharedObject/NetworkIdManager.h" #include "sharedObject/ObjectTemplateList.h" #include "sharedObject/SlotIdManager.h" #include "sharedObject/SlottedContainer.h" #include "sharedObject/SlottedContainmentProperty.h" #include "sharedObject/StructureFootprint.h" #include "sharedObject/VolumeContainer.h" #include "sharedUtility/DataTable.h" #include "sharedUtility/DataTableManager.h" #ifdef _DEBUG #include "serverGame/ConsoleManager.h" #endif #include #include #include #pragma warning(disable:4355) namespace TangibleObjectNamespace { int const s_minCombatDuration = 4; const int NUM_DAMAGE_TYPES = 9; const int s_ignoreDamageTypes = static_cast(ServerArmorTemplate::DT_environmental_heat) | static_cast(ServerArmorTemplate::DT_environmental_cold) | static_cast(ServerArmorTemplate::DT_environmental_acid) | static_cast(ServerArmorTemplate::DT_environmental_electrical); bool isEmpty(const ServerObject& obj) { const Container* container = obj.getContainerProperty(); if (container) { return (container->getNumberOfItems() == 0); } return true; } std::string const s_attributeBonusNames[] = { "health", "health_regen", "action", "action_regen", "mind", "mind_regen" }; const char * const s_accessListScriptVar = "tangible_object.accessList"; const char * const s_guildAccessListScriptVar = "tangible_object.guildAccessList"; void getCategorizedSkillModBonuses(TangibleObject const & to, std::string const & objvarName, std::map & skillModBonuses); int getCategorizedSkillModBonus(TangibleObject const & to, std::string const & objvarName, std::string const & skillMod); } //---------------------------------------------------------------------- void TangibleObjectNamespace::getCategorizedSkillModBonuses(TangibleObject const & to, std::string const & objvarName, std::map & skillModBonuses) { DynamicVariableList::NestedList children(to.getObjVars(), objvarName); for (DynamicVariableList::NestedList::const_iterator iterChildren = children.begin(); iterChildren != children.end(); ++iterChildren) { DynamicVariable::DynamicVariableType const type = iterChildren.getType(); if (type == DynamicVariable::INT) { int bonus = 0; iterChildren.getValue(bonus); std::string const childName = iterChildren.getName(); std::map::iterator iterFind = skillModBonuses.find(childName); if (iterFind != skillModBonuses.end()) iterFind->second += bonus; else IGNORE_RETURN(skillModBonuses.insert(std::make_pair(childName, bonus))); } else if (type == DynamicVariable::LIST) { std::string const childFullName = objvarName + '.' + iterChildren.getName(); getCategorizedSkillModBonuses(to, childFullName, skillModBonuses); } } } //---------------------------------------------------------------------- int TangibleObjectNamespace::getCategorizedSkillModBonus(TangibleObject const & to, std::string const & objvarName, std::string const & skillMod) { int totalBonus = 0; DynamicVariableList::NestedList children(to.getObjVars(), objvarName); for (DynamicVariableList::NestedList::const_iterator iterChildren = children.begin(); iterChildren != children.end(); ++iterChildren) { DynamicVariable::DynamicVariableType const type = iterChildren.getType(); if (type == DynamicVariable::INT) { if (iterChildren.getName() == skillMod) { int bonus = 0; iterChildren.getValue(bonus); totalBonus += bonus; } } else if (type == DynamicVariable::LIST) { std::string const childFullName = objvarName + '.' + iterChildren.getName(); totalBonus += getCategorizedSkillModBonus(to, childFullName, skillMod); } } return totalBonus; } //---------------------------------------------------------------------- using namespace TangibleObjectNamespace; //======================================================================= // class TangibleObject::CraftingToolSyncUi class TangibleObject::CraftingToolSyncUi : public ServerSynchronizedUi { public: CraftingToolSyncUi(TangibleObject & owner) : ServerSynchronizedUi(owner), m_prototype(), m_manfSchematic(), m_manfSchematicObserver() { addToUiPackage(m_prototype); addToUiPackage(m_manfSchematic); } virtual ~CraftingToolSyncUi() { if (!isOwnerChangingAuthority()) { if (m_manfSchematic.get() != CachedNetworkId::cms_cachedInvalid) { safe_cast(getOwner())->clearCraftingManufactureSchematic(); } if (m_prototype.get() != CachedNetworkId::cms_cachedInvalid) { safe_cast(getOwner())->clearCraftingPrototype(); } } } const CachedNetworkId & getPrototype(void) const { return m_prototype.get(); } void setPrototype(const CachedNetworkId & prototype) { m_prototype = prototype; } const CachedNetworkId & getManfSchematic(void) const { return m_manfSchematic.get(); } void setManfSchematic(const CachedNetworkId & manfSchematic, const CachedNetworkId & observer, bool deleteSchematic) { m_manfSchematic = manfSchematic; m_manfSchematicObserver = observer; } private: CraftingToolSyncUi(const CraftingToolSyncUi & src); CraftingToolSyncUi & operator =(const CraftingToolSyncUi & src); private: Archive::AutoDeltaVariable m_prototype; Archive::AutoDeltaVariable m_manfSchematic; CachedNetworkId m_manfSchematicObserver; }; //======================================================================= static const ConstCharCrcLowerString COMMAND_GROUP_COMBAT("combat"); // armor objvars - also defined in armor.scriptlib static const std::string OBJVAR_ARMOR_BASE = "armor"; static const std::string OBJVAR_ARMOR_ENCUMBRANCE = "armorencumbrance"; static const std::string OBJVAR_ENCUMBRANCE_SPLIT = "encumbranceSplit"; static const std::string OBJVAR_ARMOR_LEVEL = "armorLevel"; // objvar to flag the object as a shield static const std::string OBJVAR_ARMOR_SHIELD = "armor.isShield"; // armor datatable strings static const std::string DATATABLE_ARMOR = "datatables/crafting/armor.iff"; static const std::string DATATABLE_FINAL_ROW = "final"; static const std::string DATATABLE_TYPE_COL = "type"; static const std::string DATATABLE_MIN_ENCUMBRANCE_COL = "min_encumbrance"; static const std::string DATATABLE_MAX_ENCUMBRANCE_COL = "max_encumbrance"; // bio-link objvars static const std::string OBJVAR_BIO_LINK = "biolink"; static const std::string OBJVAR_BIO_LINK_ID = "biolink.id"; // This id is used for a bio-linked item that is linked to no one. // It is safe as long as no player has id 1 (which should be a good assumption). // Note this value is reflected in library.utils.scriptlib. static const NetworkId PENDING_BIO_LINK_NETWORK_ID(static_cast(1)); // template for crafting components container const static ConstCharCrcLowerString CRAFTING_COMPONENTS_CONTAINER = "object/tangible/crafting/crafting_components_container.iff"; // slots for a crafting tool const static ConstCharCrcLowerString INGREDIENT_HOPPER_SLOT_NAME = "ingredient_hopper"; const static ConstCharCrcLowerString MANF_OUTPUT_SLOT_NAME = "anythingnomod2"; const static ConstCharCrcLowerString TEMP_PROTOTYPE_SLOT_NAME = "anythingnomod1"; const static ConstCharCrcLowerString TEMP_SCHEMATIC_SLOT_NAME = "test_manf_schematic"; // objvars for crafting const static std::string OBJVAR_CRAFTING_TOOL = "crafting.tool"; const static std::string OBJVAR_CRAFTING_STATION = "crafting.station"; const static std::string OBJVAR_CRAFTING_CRAFTER = "crafting.crafter"; const static std::string OBJVAR_CRAFTING_COMPONENTS = "crafting.components"; const static std::string OBJVAR_CRAFTING_SCHEMATIC = "crafting.source_schematic"; const static std::string OBJVAR_CRAFTING_TYPE = "crafting.type"; const static std::string OBJVAR_CRAFTING_REPAIR_TYPE = "crafting.repair_type"; const static std::string OBJVAR_CRAFTING_PROTOTYPE_TIME = "crafting.prototypeTime"; const static std::string OBJVAR_CRAFTING_PROTOTYPE_START_TIME = "crafting.prototypeStartTime"; const static std::string OBJVAR_CRAFTING_PROTOTYPE_OBJECT = "crafting.prototypeObject"; const static std::string OBJVAR_CRAFTING_PROTOTYPE_CRAFTER = "crafting.prototypeCrafter"; const static std::string OBJVAR_CREATOR_ID = "crafting.creator.id"; const static std::string OBJVAR_CREATOR_XP = "crafting.creator.xp"; const static std::string OBJVAR_CREATOR_XP_TYPE = "crafting.creator.xpType"; const static std::string OBJVAR_REPAIR_TOOL = "crafting.repair"; const static std::string OBJVAR_CRAFTING_STATIONMOD = "crafting.stationMod"; const static std::string OBJVAR_SKILLMOD_BONUS = "skillmod.bonus"; const static std::string OBJVAR_CATEGORIZED_SKILLMOD_BONUS = "categorizedSkillmod.bonus"; const static std::string OBJVAR_SKILLMOD_SOCKETS = "skillmod.sockets"; const static std::string OBJVAR_ATTRIBUTE_BONUS = "attribute.bonus"; const std::string OBJVAR_COMPONENT_DATA("crafting_components"); // battlefield objvars const static std::string OBJVAR_BATTLEFIELD_PARTICIPANTS = "battleDudes"; // no trade static const std::string NOMOVE_SCRIPT = "item.special.nomove"; static const std::string OBJVAR_DECLINE_DUEL = "decline_duel"; const SharedObjectTemplate * TangibleObject::m_defaultSharedTemplate = NULL; //----------------------------------------------------------------------- TangibleObject::TangibleObject(const ServerTangibleObjectTemplate* newTemplate) : ServerObject(newTemplate), m_combatData(NULL), m_pvpType(), m_pvpMercenaryType(PvpType_Neutral), m_pvpFutureType(-1), m_pvpFaction(), m_pvpMercenaryFaction(PvpData::getNeutralFactionId()), m_pvpRegionCrc(), m_pvpEnemies(), m_damageTaken(0), m_maxHitPoints(newTemplate->getMaxHitPoints()), m_ownerId(NetworkId(static_cast(0))), m_customAppearance(), m_appearanceData(), m_locationTargets(), m_components(), m_visible(true), m_hideFromClient(false), m_count(newTemplate->getCount()), m_condition(newTemplate->getCondition()), m_creatorId(NetworkId::cms_invalid), m_sourceDraftSchematic(0), m_hateList(), m_hateOverTime(), m_inCombat(false), m_combatStartTime(0), m_attackableOverride(false), m_passiveReveal(), m_passiveRevealPlayerCharacter(), m_mapColorOverride(0), m_accessList(), m_guildAccessList(), m_effectsMap(), m_npcConversation(NULL), m_conversations() { WARNING_STRICT_FATAL(!getSharedTemplate(), ("Tried to create a TANGIBLE %s object without a shared template!\n", newTemplate->DataResource::getName())); m_hateList.addServerNpAutoDeltaVariables(m_serverPackage_np); m_hateList.setOwner(this); m_maxHitPoints.setSourceObject(this); m_ownerId.setSourceObject(this); m_customAppearance.setSourceObject(this); m_appearanceData.setSourceObject(this); m_pvpType.setSourceObject(this); m_pvpMercenaryType.setSourceObject(this); m_pvpFaction.setSourceObject(this); m_pvpMercenaryFaction.setSourceObject(this); m_pvpRegionCrc.setSourceObject(this); m_pvpEnemies.setSourceObject(this); m_visible.setSourceObject(this); m_hideFromClient.setSourceObject(this); m_condition.setSourceObject(this); m_inCombat.setSourceObject(this); m_attackableOverride.setSourceObject(this); m_passiveRevealPlayerCharacter.setSourceObject(this); // set to not disabled, and set invulnerable and wantSawAttack from template int condition = m_condition.get(); condition &= ~( ServerTangibleObjectTemplate::C_disabled | ServerTangibleObjectTemplate::C_invulnerable | ServerTangibleObjectTemplate::C_wantSawAttackTrigger); if (newTemplate->getInvulnerable()) condition |= ServerTangibleObjectTemplate::C_invulnerable; if (newTemplate->getWantSawAttackTriggers()) condition |= ServerTangibleObjectTemplate::C_wantSawAttackTrigger; m_condition = condition; // attach trigger volumes to this object size_t i; size_t count = newTemplate->getTriggerVolumesCount(); for (i = 0; i < count; ++i) { const TriggerVolumeData & trigger = newTemplate->getTriggerVolumes(static_cast(i)); createTriggerVolume(trigger.getRadius(), trigger.getName(), true); //@todo : add "promiscuous param to template entry } //-- initialize CustomizationDataProperty if any customization variables are declared // for the SharedTangibleObjectTemplate. const SharedTangibleObjectTemplate *const sharedObjectTemplate = safe_cast(getSharedTemplate()); if (sharedObjectTemplate) { //-- create CustomizationDataProperty if appropriate sharedObjectTemplate->createCustomizationDataPropertyAsNeeded(*this); //-- if a CustomizationDataProperty was created, attach a modification listener. // check if the property exists CustomizationDataProperty *cdProperty = safe_cast(getProperty(CustomizationDataProperty::getClassPropertyId())); if (cdProperty) { // retrieve the CustomizationData instance associated with the property CustomizationData *const customizationData = cdProperty->fetchCustomizationData(); NOT_NULL(customizationData); // attach modification callback customizationData->registerModificationListener(customizationDataModificationCallback, this); // release local reference customizationData->release(); } if(ConfigServerGame::getCreateAppearances()) { const std::string &appearanceString = sharedObjectTemplate->getAppearanceFilename(); if (appearanceString.size() != 0 && strstr (appearanceString.c_str (), ".apt") != 0) { Appearance * newAppearance = AppearanceTemplateList::createAppearance(appearanceString.c_str()); if(newAppearance != NULL) { setAppearance(newAppearance); } } } } // If the object is a path node, attach the pathfinding notification if (isWaypoint()) { addNotification( ServerPathfindingNotification::getInstance() ); } // attach the collision property ServerCollisionProperty* collision = new ServerCollisionProperty(*this,getSharedTemplate()); addProperty(*collision); addMembersToPackages(); ObjectTracker::addTangible(); if(getGameObjectType() == SharedObjectTemplate::GOT_loadbeacon) { ServerWorld::addLoadBeacon(this); } //-- add the lot manager notification { SharedTangibleObjectTemplate const * const sharedTangibleObjectTemplate = dynamic_cast (getSharedTemplate ()); if (sharedTangibleObjectTemplate) { StructureFootprint const * const structureFootprint = sharedTangibleObjectTemplate->getStructureFootprint (); if (structureFootprint) addNotification (ServerLotManagerNotification::getInstance ()); } else DEBUG_WARNING (true, ("TangibleObject::TangibleObject - [%s] does not have a shared tangible object template", getObjectTemplate ()->getName ())); } } //----------------------------------------------------------------------- TangibleObject::~TangibleObject() { //-- This must be the first line in the destructor to invalidate any watchers watching this object nullWatchers(); if (getSynchronizedUi() != NULL) { CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync != NULL) { if (isAuthoritative() && isCraftingTool()) { clearCraftingPrototype(); clearCraftingManufactureSchematic(); } else { if (!isAuthoritative()) { WARNING(true, ("Tangible object %s is not authoritative " "but has a SynchronizedUi", getNetworkId().getValueString().c_str())); } if (!isCraftingTool()) { WARNING(true, ("Tangible object %s is not a crafting tool " "but has a CraftingToolSyncUi", getNetworkId().getValueString().c_str())); } sync->setPrototype(CachedNetworkId::cms_cachedInvalid); sync->setManfSchematic(CachedNetworkId::cms_cachedInvalid, CachedNetworkId::cms_cachedInvalid, true); } } } if(getGameObjectType() == SharedObjectTemplate::GOT_loadbeacon) { ServerWorld::removeLoadBeacon(this); } CombatTracker::removeDefender(this); if (m_combatData != NULL) { if (isAuthoritative()) { if (!getCombatData()->defenseData.damage.empty()) { WARNING(true, ("\tobject still has a non-empty damage list")); // look up the call stack information const int callStackOffset = 4; const int maxStackDepth = 64; const int callStackSize = callStackOffset + maxStackDepth; uint32 callStack[callStackOffset + maxStackDepth]; DebugHelp::getCallStack(callStack, callStackOffset + maxStackDepth); // look up the caller's file and line if (callStack[callStackOffset]) { char lib[4 * 1024] = { '\0' }; char file[4 * 1024] = { '\0' }; int line = 0; REPORT_LOG(true, ("\tCall stack:")); for (int i = callStackOffset; i < callStackSize; ++i) { if (callStack[i]) { if (DebugHelp::lookupAddress(callStack[i], lib, file, sizeof(file), line)) REPORT_LOG(true, ("\t%s(%d) : caller %d\n", file, line, i-callStackOffset)); else REPORT_LOG(true, ("\tunknown(0x%08X) : caller %d\n", static_cast(callStack[i]), i-callStackOffset)); } } } } } delete m_combatData; m_combatData = NULL; if (!isPlayerControlled()) ObjectTracker::removeCombatAI(); } if (m_npcConversation != NULL) { delete m_npcConversation; m_npcConversation = NULL; } ObjectTracker::removeTangible(); } // TangibleObject::~TangibleObject //----------------------------------------------------------------------- /** * Initializes a newly created object. */ void TangibleObject::initializeFirstTimeObject() { DEBUG_FATAL(!isAuthoritative(), ("Trying to initialize non auth object %s", getNetworkId().getValueString().c_str())); // set up armor from the template const ServerTangibleObjectTemplate * newTemplate = safe_cast< const ServerTangibleObjectTemplate *>(getObjectTemplate()); if (newTemplate != NULL) { setInvulnerable(newTemplate->getInvulnerable()); if (isAuthoritative()) { initializeVisibility(); const ServerArmorTemplate * armorTemplate = newTemplate->getArmor(); if (armorTemplate != NULL) { int const rating = static_cast(armorTemplate->getRating()); if ( rating < static_cast(ServerArmorTemplate::AR_armorNone) || rating > static_cast(ServerArmorTemplate::AR_armorHeavy)) { WARNING(true, ("TangibleObject::initializeFirstTimeObject trying " "to add armor data from template %s with an invalid rating " "of %d", armorTemplate->getName(), rating)); } else { // armor integrity replaces hitpoints m_maxHitPoints = armorTemplate->getIntegrity(); } } } } // attach the combat skeleton script to this object ServerTangibleObjectTemplate::CombatSkeleton skeleton = static_cast(getCombatSkeleton()); const ConfigCombatEngine::SkeletonAttackMod & skeletonMod = ConfigCombatEngine::getSkeletonAttackMod(skeleton); if (!skeletonMod.script.empty() && skeletonMod.script[0] != '\0') { NOT_NULL(getScriptObject()); getScriptObject()->attachScript(skeletonMod.script, true); } handleContainerSetup(); ServerObject::initializeFirstTimeObject(); setDefaultAlterTime(AlterResult::cms_keepNoAlter); calcPvpableState(); if (!isNonPvpObject()) MessageToQueue::sendRecurringMessageToC(getNetworkId(), "C++CheckPvpRegion", std::string(), static_cast(getPvpRegionCheckTime())); } //----------------------------------------------------------------------- /** * Initializes a proxy object. */ void TangibleObject::endBaselines() { ServerObject::endBaselines(); handleContainerSetup(); // set the hate list owner to ensure the player object is set correctly m_hateList.setOwner(this); if (isAuthoritative()) { // fix the load_with values if they don't match if ( getLoadWith() != ContainerInterface::getLoadWithContainerId(*this)) { setLoadWith(ContainerInterface::getLoadWithContainerId(*this)); } // test to flag the object as disabled if (getDamageTaken() >= getMaxHitPoints()) setDisabled(true); // pvp needs to be notified after an object is both initialized and made authoritative. Pvp::handleAuthorityAcquire(*this); // this code is for legacy objects already in the database // Don't consider virtual overrides for invulnerability in this case if (getCacheVersion() > 0 && !TangibleObject::isInvulnerable()) setInvulnerable(true); if (getObjVars().hasItem(OBJVAR_CRAFTING_SCHEMATIC)) setCondition(C_crafted); else clearCondition(C_crafted); removeObjVarItem("debuggingLogCommandEnqueue"); // destroy duped TCG Set5 AT-AT Head ITV if ((getStaticItemName() == "item_tcg_loot_reward_series5_atat_head_itv") && !getObjVars().hasItem("rewardGrantInfo")) { MessageToQueue::getInstance().sendMessageToC(getNetworkId(), "C++DestroyDupedTcg5AtAtHeadItv", "", 5, false); } } calcPvpableState(); // check for existence of objvar to enable/disable m_logCommandEnqueue CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { queue->setLogCommandEnqueue(getObjVars().hasItem("debuggingLogCommandEnqueue")); } // if this is an incubator, and it contains imported resource from another // galaxy, register the imported resource type on this game server, if it // hasn't already been registered on this game server; this must be done // on each game server as it receives the incubator static const uint32 crcIncubator = CrcLowerString::calculateCrc("object/tangible/crafting/station/incubator_station.iff"); if (getTemplateCrc() == crcIncubator) { DynamicVariableList const & objvars = getObjVars(); if (objvars.hasItem("power.resourceData") && (DynamicVariable::STRING == objvars.getType("power.resourceData"))) { std::string resourceData; IGNORE_RETURN(objvars.getItem("power.resourceData", resourceData)); IGNORE_RETURN(ResourceTypeObject::addImportedResourceType(resourceData)); } } } // TangibleObject::endBaselines //----------------------------------------------------------------------- void TangibleObject::onLoadedFromDatabase() { ServerObject::onLoadedFromDatabase(); // if we are a crafting tool that is counting down a prototype, // reactivate the counter if (isCraftingTool() && isAuthoritative()) { float prototypeTime = 0; getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_TIME, prototypeTime); if (prototypeTime > 0) { int timeLeft = getCraftingToolPrototypeTime(); if (timeLeft < 1) timeLeft = 1; NetworkId ownerId; NetworkId prototypeId; bool result = getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_OBJECT, prototypeId) && getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_CRAFTER, ownerId); if (result) { ScriptParams params; params.addParam(ownerId, "crafter"); params.addParam(prototypeId, "prototype"); ScriptDictionaryPtr dictionary; getScriptObject()->makeScriptDictionary(params, dictionary); if (dictionary.get() != NULL) { dictionary->serialize(); MessageToQueue::getInstance().sendMessageToJava(getNetworkId(), "prototypeDone", dictionary->getSerializedData(), timeLeft, false); MessageToQueue::getInstance().sendMessageToC(getNetworkId(), "C++updateCounter", "", 1, false); } } } } setDefaultAlterTime(AlterResult::cms_keepNoAlter); calcPvpableState(); if (!isNonPvpObject()) MessageToQueue::sendRecurringMessageToC(getNetworkId(), "C++CheckPvpRegion", std::string(), static_cast(getPvpRegionCheckTime())); if(isLocked()) { readInUserAccessListObjVars(); readInGuildAccessListObjVars(); } // Invalid Socket check. bool checkForInvalidSockets = (GameObjectTypes::isTypeOf(getGameObjectType(), SharedObjectTemplate::GOT_clothing) || GameObjectTypes::isTypeOf(getGameObjectType(), SharedObjectTemplate::GOT_armor) ) && isCrafted(); if(checkForInvalidSockets) { std::vector< std::pair< std::string, int > > skillMods; getSkillModBonuses(skillMods, false); if(skillMods.size() > 3) // 3 is the max legit amount for any 1 socket item { // Check all our stats, make sure they aren't over the limit for 1 socket, cross reference names with the reverse engineering datatable. DataTable * modTable = DataTableManager::getTable("datatables/crafting/reverse_engineering_mods.iff", true); if(!modTable) return; int invalidSkillCounter = 0; // Check each skill mod against the datatable. If we don't have that skill mod in the table, then it could be a legitimate item from the older system. for(unsigned int i = 0; i < skillMods.size(); ++i) { std::string skillModName = skillMods[i].first; int row = modTable->searchColumnString(0, skillModName); if(row < 0) // wasn't in our table, could be legit. continue; ++invalidSkillCounter; } if(invalidSkillCounter <= 3) // Valid item. return; // Player Name std::string playerName = NameManager::getInstance().getPlayerFullName(getOwnerId()); Unicode::String objName = getObjectName(); std::string narrowObjName; if(!objName.empty()) narrowObjName = Unicode::wideToNarrow(objName); // Possible invalid item. LOG("CustomerService", ("extraSocketExploit:Reason: Invalid # of Skill mods. Item Name: [%s] OID:[%s] Shared Template Name: [%s]", narrowObjName.empty() ? "" : narrowObjName.c_str(), getNetworkId().getValueString().c_str(), getSharedTemplateName() ? getSharedTemplateName() : "")); LOG("CustomerService", ("extraSocketExploit:Owner OID: [%s] Owner Station ID: [%u] Owner Name: [%s]", getOwnerId().getValueString().c_str(), NameManager::getInstance().getPlayerStationId(getOwnerId()), playerName.empty() ? "" : playerName.c_str())); LOG("CustomerService", ("extraSocketExploit:Skill Mods are:")); for(unsigned int i = 0; i < skillMods.size(); ++i) { int row = modTable->searchColumnString(0, skillMods[i].first); int currentValue = skillMods[i].second; int maxValue = 0; if(row < 0) { LOG("CustomerService", ("extraSocketExploit:Skill Mod: [%s] Value: [%d]", skillMods[i].first.c_str(), skillMods[i].second)); continue; } int ratioValue = modTable->getIntValue(1, row); if(ratioValue != 0) maxValue = 35 / ratioValue; if(currentValue > maxValue) LOG("CustomerService", ("extraSocketExploit:(Invalid)Skill Mod: [%s] Value: [%d] Max Legal Value [%d]", skillMods[i].first.c_str(), skillMods[i].second, maxValue)); else if(currentValue > 35) LOG("CustomerService", ("extraSocketExploit:(Invalid)Skill Mod: [%s] Value: [%d] Max Legal Value [35]", skillMods[i].first.c_str(), skillMods[i].second)); else LOG("CustomerService", ("extraSocketExploit:Skill Mod: [%s] Value: [%d]", skillMods[i].first.c_str(), skillMods[i].second)); } } else { // Check all our stats, make sure they aren't over the limit for 1 socket. DataTable * modTable = DataTableManager::getTable("datatables/crafting/reverse_engineering_mods.iff", true); if(!modTable) return; for(unsigned int i = 0; i < skillMods.size(); ++i) { std::string skillModName = skillMods[i].first; int currentValue = skillMods[i].second; int maxValue = 0; bool logItem = false; if(currentValue > 35) logItem = true; else { int row = modTable->searchColumnString(0, skillModName); if(row < 0) return; int ratioValue = modTable->getIntValue(1, row); if(ratioValue != 0) maxValue = 35 / ratioValue; if(currentValue > maxValue) logItem = true; } if(logItem) { // Player Name std::string playerName = NameManager::getInstance().getPlayerFullName(getOwnerId()); Unicode::String objName = getObjectName(); std::string narrowObjName; if(!objName.empty()) narrowObjName = Unicode::wideToNarrow(objName); // Possible invalid item. LOG("CustomerService", ("extraSocketExploit:Reason: Invalid Skill mod value. Item Name: [%s] OID:[%s] Shared Template Name: [%s]", narrowObjName.empty() ? "" : narrowObjName.c_str(), getNetworkId().getValueString().c_str(), getSharedTemplateName() ? getSharedTemplateName() : "")); LOG("CustomerService", ("extraSocketExploit:Owner OID: [%s] Owner Station ID: [%u] Owner Name: [%s]", getOwnerId().getValueString().c_str(), NameManager::getInstance().getPlayerStationId(getOwnerId()), playerName.empty() ? "" : playerName.c_str())); LOG("CustomerService", ("extraSocketExploit:Skill Mods are:")); for(unsigned int j = 0; j < skillMods.size(); ++j) { int row = modTable->searchColumnString(0, skillMods[j].first); currentValue = skillMods[j].second; if(row < 0) { LOG("CustomerService", ("extraSocketExploit:Skill Mod: [%s] Value: [%d]", skillMods[j].first.c_str(), skillMods[j].second)); continue; } int ratioValue = modTable->getIntValue(1, row); if(ratioValue != 0) maxValue = 35 / ratioValue; if(currentValue > maxValue) LOG("CustomerService", ("extraSocketExploit:(Invalid)Skill Mod: [%s] Value: [%d] Max Legal Value [%d]", skillMods[j].first.c_str(), skillMods[j].second, maxValue)); else if(currentValue > 35) LOG("CustomerService", ("extraSocketExploit:(Invalid)Skill Mod: [%s] Value: [%d] Max Legal Value [35]", skillMods[j].first.c_str(), skillMods[j].second)); else LOG("CustomerService", ("extraSocketExploit:Skill Mod: [%s] Value: [%d]", skillMods[j].first.c_str(), skillMods[j].second)); } return; } } } } // start the decay process going } //----------------------------------------------------------------------- void TangibleObject::virtualOnSetAuthority() { ServerObject::virtualOnSetAuthority(); } //----------------------------------------------------------------------- TangibleObject * TangibleObject::getTangibleObject(NetworkId const & networkId) { return asTangibleObject(NetworkIdManager::getObjectById(networkId)); } //----------------------------------------------------------------------- TangibleObject * TangibleObject::asTangibleObject(Object * object) { ServerObject * serverObject = (object != NULL) ? object->asServerObject() : NULL; return (serverObject != NULL) ? serverObject->asTangibleObject() : NULL; } //----------------------------------------------------------------------- TangibleObject const * TangibleObject::asTangibleObject(Object const * object) { ServerObject const * serverObject = (object != NULL) ? object->asServerObject() : NULL; return (serverObject != NULL) ? serverObject->asTangibleObject() : NULL; } //----------------------------------------------------------------------- TangibleObject * TangibleObject::asTangibleObject() { return this; } // ---------------------------------------------------------------------- TangibleObject const * TangibleObject::asTangibleObject() const { return this; } // ---------------------------------------------------------------------- /** * Returns a shared template if none was given for this object. * * @return the shared template */ const SharedObjectTemplate * TangibleObject::getDefaultSharedTemplate(void) const { static const ConstCharCrcLowerString templateName("object/tangible/base/shared_tangible_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, "TangibleObject::removeDefaultTemplate"); } return m_defaultSharedTemplate; } // TangibleObject::getDefaultSharedTemplate //----------------------------------------------------------------------- /** * Cleans up the default shared template. */ void TangibleObject::removeDefaultTemplate(void) { if (m_defaultSharedTemplate != NULL) { m_defaultSharedTemplate->releaseReference(); m_defaultSharedTemplate = NULL; } } // TangibleObject::removeDefaultTemplate //----------------------------------------------------------------------- /** * Returns the name of the shared template that will be sent to the client when * this object is created. If this object has a custom appearance, that template * name will be returned, otherwise the default name will be returned. * * */ const char * TangibleObject::getClientSharedTemplateName(void) const { if (!m_customAppearance.get().empty()) return m_customAppearance.get().c_str(); return ServerObject::getClientSharedTemplateName(); } // TangibleObject::getClientSharedTemplateName //----------------------------------------------------------------------- void TangibleObject::addLocationTarget(const LocationData & location) { if(isAuthoritative()) { std::vector::const_iterator i; for(i = m_locationTargets.begin(); i != m_locationTargets.end(); ++i) { if((*i).name == location.name) { m_locationTargets.set(static_cast(std::distance(m_locationTargets.begin(), i)), location); return; } } m_locationTargets.push_back(location); scheduleForAlter(); } else { sendControllerMessageToAuthServer(CM_addLocationTarget, new MessageQueueGenericValueType(location)); } } //----------------------------------------------------------------------- void TangibleObject::removeLocationTarget(const Unicode::String & locationName) { if(isAuthoritative()) { std::vector::const_iterator i; for(i = m_locationTargets.begin(); i != m_locationTargets.end(); ++i) { if((*i).name == locationName) { m_locationTargets.erase(static_cast(std::distance(m_locationTargets.begin(), i))); return; } } } else { sendControllerMessageToAuthServer(CM_removeLocationTarget, new MessageQueueGenericValueType(locationName)); } } //----------------------------------------------------------------------- void TangibleObject::onArrivedAtLocation(const LocationData & location) { // only invokes a script trigger, which is automagically forwarded to // the authoritative server if(getScriptObject()) { ScriptParams p; p.addParam(location.name); IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_ARRIVE_AT_LOCATION, p)); } } // ---------------------------------------------------------------------- /** * This function is called after the TangibleObject has received a * new custom appearance string. * * @arg value the value of the new custom appearance string */ void TangibleObject::customAppearanceModified(const std::string& value) { // we don't value to use the value arg since we already have the // data stored in m_customAppearance, but just in case the interface // changes, we'll use it here. if (value.empty()) return; } // TangibleObject::customAppearanceModified // ---------------------------------------------------------------------- /** * This function is called after the TangibleObject has received a * new appearance data customization string. * * @arg value the value of the new appearance customization string */ // ---------------------------------------------------------------------- void TangibleObject::appearanceDataModified(const std::string& value) { // we don't value to use the value arg since we already have the // data stored in m_appearanceData, but just in case the interface // changes, we'll use it here. Technically we don't need to keep // the m_appearanceData string around on the client. We can build // it at will from the appearance. //-- skip no-content customization data if (value.empty()) return; // check if the property exists CustomizationDataProperty *cdProperty = safe_cast(getProperty(CustomizationDataProperty::getClassPropertyId())); if (!cdProperty) { // every debug warning I put here goes into never-never land in addr2line. real handy feature... // DEBUG_WARNING(true, ("non-zero-length appearance string sent to object that doesn't declare any customization variables in its template.")); // DEBUG_WARNING(true, ("received appear string for non-customizable object.")); return; } // retrieve the CustomizationData instance associated with the property CustomizationData *const customizationData = cdProperty->fetchCustomizationData(); NOT_NULL(customizationData); //-- initialize CustomizationData variable values from the string customizationData->loadLocalDataFromString(value); //-- release local reference customizationData->release(); } //----------------------------------------------------------------------- /** * Gets all the equipped items corresponding to a combat skeleton "bone". * * @param combatBone the bone we want equipment for * @param items list to be filled in with the items */ void TangibleObject::getEquippedItems(uint32 combatBone, std::vector &items) const { SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); if (container == NULL) return; std::vector itemIds; container->getObjectsForCombatBone(combatBone, itemIds); std::vector::const_iterator iter; for (iter = itemIds.begin(); iter != itemIds.end(); ++iter) { Object * object = (*iter).getObject(); if (object != NULL) { TangibleObject * item = dynamic_cast(object); if (item != NULL) items.push_back(item); } } } // TangibleObject::getEquippedItems //----------------------------------------------------------------------- /** * Returns a random equipped item corresponding to a combat skeleton "bone". * * @param combatBone the bone we want the item for * * @return the item */ TangibleObject * TangibleObject::getRandomEquippedItem(uint32 combatBone) const { std::vector items; getEquippedItems(combatBone, items); if (items.empty()) { return NULL; } int index = Random::random(0, items.size() - 1); return items[index]; } // TangibleObject::getRandomEquippedItem //----------------------------------------------------------------------- /** * Updates the object for damage taken in battle. * * @param time current game time * * @return base class result */ float TangibleObject::alter(real time) { PROFILER_AUTO_BLOCK_DEFINE("TangibleObject::alter"); if (isAuthoritative()) { // Determine the combat state of the object { CombatEngine::alter(*this); m_hateList.alter(); if (!m_hateList.isEmpty()) { if (!isPlayerControlled()) { // AI // Schedule the AI for an update { AiCombatPulseQueue::schedule(this, 0, static_cast(time * 1000.0f)); } } else { // Player // Make sure the hate list is purged of invalid hate targets. If a player is no // longer hated by a hate target, then remove that target from the player's list. // A player must be in their hate target's hate list and vice versa. { typedef std::vector PurgeList; static PurgeList purgeList; purgeList.clear(); HateList::UnSortedList const & unSortedHateList = getUnSortedHateList(); HateList::UnSortedList::const_iterator iterUnSortedHateList = unSortedHateList.begin(); for (; iterUnSortedHateList != unSortedHateList.end(); ++iterUnSortedHateList) { CachedNetworkId const & id = iterUnSortedHateList->first; if (!isHatedBy(id.getObject())) { purgeList.push_back(id); } } PurgeList::const_iterator iterPurgeList = purgeList.begin(); for (; iterPurgeList != purgeList.end(); ++iterPurgeList) { removeHateTarget(*iterPurgeList); } } } } if (m_hateList.isEmpty()) { if ( !isPlayerControlled() || (getCombatDuration() > s_minCombatDuration)) { setInCombat(false); } } } } // check for location target arrival if(isAuthoritative() && !m_locationTargets.empty()) { Vector const effectiveWorldLocation = findPosition_w(); for (std::vector::const_iterator i = m_locationTargets.begin(); i != m_locationTargets.end(); ++i) { if ((*i).scene == ServerWorld::getSceneId()) { Sphere const &s = (*i).location; // 3d targets are denoted by y coordinates > 50k, with 100k added to them // If it's a 3d target, add 100k to our y coordinate // If it's a 2d target, set our y coordinate to the target's // 2d comparison, put locations on same plane Vector v = effectiveWorldLocation; if (s.getCenter().y > 50000.0f) v.y += 100000.0f; else v.y = s.getCenter().y; if (s.contains(v)) { // copy the location data so we can remove it from the vector prior to triggerring LocationData triggeredLocation(*i); removeLocationTarget((*i).name); // trigger any scripts onArrivedAtLocation(triggeredLocation); // we'll catch any additional location triggers going off during the next alter break; } } } } //-- handle modified customization data if (isCustomizationDataModified()) { // associated CustomizationData instance is modified. Pack CustomizationData // into m_appearanceData string, which will get sent to others. //-- get property CustomizationDataProperty *cdProperty = safe_cast(getProperty(CustomizationDataProperty::getClassPropertyId())); if (!cdProperty) { DEBUG_WARNING(true, ("customization data modified but no customization data!")); } else { // retrieve the CustomizationData instance associated with the property CustomizationData *const customizationData = cdProperty->fetchCustomizationData(); NOT_NULL(customizationData); //-- initialize CustomizationData variable values from the string m_appearanceData.set(customizationData->writeLocalDataToString()); //-- release reference customizationData->release(); } // reset flag setCustomizationDataModified(false); } return ServerObject::alter(time); } // TangibleObject::alter //----------------------------------------------------------------------- float TangibleObject::getNextAlterTime(float baseAlterTime) const { if (baseAlterTime == AlterResult::cms_kill || baseAlterTime == AlterResult::cms_alterNextFrame) return baseAlterTime; float result = baseAlterTime; if (getSynchronizedUi() && dynamic_cast(getSynchronizedUi())) return AlterResult::cms_alterNextFrame; if (isAuthoritative()) { if (isInCombat()) return AlterResult::cms_alterNextFrame; if (!m_locationTargets.empty()) AlterResult::incorporateAlterResult(result, ConfigServerGame::getLocationTargetCheckIntervalSec()); } return result; } //----------------------------------------------------------------------- void TangibleObject::conclude() { PROFILER_AUTO_BLOCK_DEFINE("TangibleObject::conclude"); // if we are a crafting tool, conclude our prototype and manf schematic if (getSynchronizedUi() != NULL) { const CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync != NULL) { Object * object = sync->getPrototype().getObject(); if (object != NULL) object->conclude(); object = sync->getManfSchematic().getObject(); if (object != NULL) object->conclude(); } } ServerObject::conclude(); } //----------------------------------------------------------------------- float TangibleObject::getPvpRegionCheckTime() { return ConfigServerGame::getTangiblePvpRegionCheckTime(); } //----------------------------------------------------------------------- void TangibleObject::setPvpType(Pvp::PvpType pvpType) { FATAL(!isAuthoritative(), ("Inappropriate use of TangibleObject::setPvpType - this should only be called from the Pvp interface.")); Pvp::PvpType oldType = static_cast(m_pvpType.get()); if (oldType != pvpType) { m_pvpType.set(static_cast(pvpType)); m_pvpFutureType.set(-1); if (getScriptObject()) { ScriptParams params; params.addParam(oldType); params.addParam(pvpType); IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_PVP_TYPE_CHANGED, params)); } } } //----------------------------------------------------------------------- void TangibleObject::setPvpFutureType(Pvp::PvpType pvpFutureType) { FATAL(!isAuthoritative(), ("Inappropriate use of TangibleObject::setPvpFutureType - this should only be called from the Pvp interface.")); Pvp::PvpType oldFutureType = static_cast(m_pvpFutureType.get()); if (oldFutureType != pvpFutureType) { m_pvpFutureType.set(static_cast(pvpFutureType)); } } //----------------------------------------------------------------------- void TangibleObject::setPvpFaction(Pvp::FactionId factionId) { FATAL(!isAuthoritative(), ("Inappropriate use of TangibleObject::setPvpFaction - this should only be called from the Pvp interface.")); Pvp::FactionId oldFaction = static_cast(m_pvpFaction.get()); if (oldFaction != factionId) { m_pvpFaction.set(static_cast(factionId)); if (getScriptObject()) { ScriptParams params; params.addParam(static_cast(oldFaction)); params.addParam(static_cast(factionId)); IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_PVP_FACTION_CHANGED, params)); } // if player character is changing faction, clear // out current accumulated CGW points and rating; // this *MUST* be done after m_pvpFaction has // been set to the new faction because // modifyCurrentGcwRating() uses the faction information CreatureObject * creature = asCreatureObject(); if (creature) { PlayerObject * player = PlayerCreatureController::getPlayerObject(creature); if (player) { // decrementing the value by a really large // number will cause it to reset to 0 static int const min = std::numeric_limits::min(); player->modifyCurrentGcwPoints(min, false); player->modifyCurrentGcwRating(min, true); // if player is no longer factional, reset current pvp kills if (!PvpData::isImperialFactionId(factionId) && !PvpData::isRebelFactionId(factionId)) player->modifyCurrentPvpKills(min, false); // if new faction is not neutral, remove any mercenary status if (!PvpData::isNeutralFactionId(factionId)) Pvp::setNeutralMercenaryFaction(*creature, PvpData::getNeutralFactionId(), PvpType_Neutral); } } } } //----------------------------------------------------------------------- void TangibleObject::setPvpMercenaryFaction(Pvp::FactionId factionId, Pvp::PvpType pvpType) { FATAL(!isAuthoritative(), ("Inappropriate use of TangibleObject::setPvpMercenaryFaction - this should only be called from the Pvp interface.")); // this only applies to neutral if (!PvpData::isNeutralFactionId(m_pvpFaction.get()) && !PvpData::isNeutralFactionId(factionId)) return; // this only applies to player character PlayerObject * const po = PlayerCreatureController::getPlayerObject(asCreatureObject()); if (!po || !po->isAuthoritative()) return; // see if mercenary functionality is allowed if (!PvpData::isNeutralFactionId(factionId)) { if (PvpData::isImperialFactionId(factionId)) { if (pvpType == PvpType_Covert) { if (!ConfigServerGame::getEnableCovertImperialMercenary()) return; po->modifyCollectionSlotValue("covert_imperial_mercenary", 1); po->modifyCollectionSlotValue("overt_imperial_mercenary", -1); po->modifyCollectionSlotValue("covert_rebel_mercenary", -1); po->modifyCollectionSlotValue("overt_rebel_mercenary", -1); std::string const & currentTitle = po->getTitle(); if (po->hasCompletedCollectionSlot("covert_imperial_mercenary")) { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle("covert_imperial_mercenary"); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle("covert_imperial_sympathizer"); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle("covert_imperial_loyalist"); } else { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle(std::string()); } } else if (pvpType == PvpType_Declared) { if (!ConfigServerGame::getEnableOvertImperialMercenary()) return; po->modifyCollectionSlotValue("covert_imperial_mercenary", -1); po->modifyCollectionSlotValue("overt_imperial_mercenary", 1); po->modifyCollectionSlotValue("covert_rebel_mercenary", -1); po->modifyCollectionSlotValue("overt_rebel_mercenary", -1); std::string const & currentTitle = po->getTitle(); if (po->hasCompletedCollectionSlot("overt_imperial_mercenary")) { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle("overt_imperial_mercenary"); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle("overt_imperial_sympathizer"); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle("overt_imperial_loyalist"); } else { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle(std::string()); } } else { return; } } else if (PvpData::isRebelFactionId(factionId)) { if (pvpType == PvpType_Covert) { if (!ConfigServerGame::getEnableCovertRebelMercenary()) return; po->modifyCollectionSlotValue("covert_imperial_mercenary", -1); po->modifyCollectionSlotValue("overt_imperial_mercenary", -1); po->modifyCollectionSlotValue("covert_rebel_mercenary", 1); po->modifyCollectionSlotValue("overt_rebel_mercenary", -1); std::string const & currentTitle = po->getTitle(); if (po->hasCompletedCollectionSlot("covert_rebel_mercenary")) { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle("covert_rebel_mercenary"); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle("covert_rebel_sympathizer"); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle("covert_rebel_insurgent"); } else { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle(std::string()); } } else if (pvpType == PvpType_Declared) { if (!ConfigServerGame::getEnableOvertRebelMercenary()) return; po->modifyCollectionSlotValue("covert_imperial_mercenary", -1); po->modifyCollectionSlotValue("overt_imperial_mercenary", -1); po->modifyCollectionSlotValue("covert_rebel_mercenary", -1); po->modifyCollectionSlotValue("overt_rebel_mercenary", 1); std::string const & currentTitle = po->getTitle(); if (po->hasCompletedCollectionSlot("overt_rebel_mercenary")) { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle("overt_rebel_mercenary"); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle("overt_rebel_sympathizer"); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle("overt_rebel_insurgent"); } else { if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer")) po->setTitle(std::string()); else if ((currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) po->setTitle(std::string()); } } else { return; } } else { return; } } else { po->modifyCollectionSlotValue("covert_imperial_mercenary", -1); po->modifyCollectionSlotValue("overt_imperial_mercenary", -1); po->modifyCollectionSlotValue("covert_rebel_mercenary", -1); po->modifyCollectionSlotValue("overt_rebel_mercenary", -1); std::string const & currentTitle = po->getTitle(); if ((currentTitle == "covert_imperial_mercenary") || (currentTitle == "overt_imperial_mercenary") || (currentTitle == "covert_rebel_mercenary") || (currentTitle == "overt_rebel_mercenary") || (currentTitle == "covert_imperial_sympathizer") || (currentTitle == "overt_imperial_sympathizer") || (currentTitle == "covert_rebel_sympathizer") || (currentTitle == "overt_rebel_sympathizer") || (currentTitle == "covert_imperial_loyalist") || (currentTitle == "overt_imperial_loyalist") || (currentTitle == "covert_rebel_insurgent") || (currentTitle == "overt_rebel_insurgent")) { po->setTitle(std::string()); } } // flag when the player stopped being a factional helper for the particular faction if (m_pvpMercenaryFaction.get() != factionId) { if (PvpData::isImperialFactionId(m_pvpMercenaryFaction.get())) { IGNORE_RETURN(setObjVarItem("factionalHelper.timeStopHelpingImperial", static_cast(::time(NULL)))); removeObjVarItem("factionalHelper.timeStopHelpingRebel"); } else if (PvpData::isRebelFactionId(m_pvpMercenaryFaction.get())) { IGNORE_RETURN(setObjVarItem("factionalHelper.timeStopHelpingRebel", static_cast(::time(NULL)))); removeObjVarItem("factionalHelper.timeStopHelpingImperial"); } } m_pvpMercenaryFaction.set(static_cast(factionId)); m_pvpMercenaryType.set(((PvpData::isNeutralFactionId(factionId)) ? PvpType_Neutral : static_cast(pvpType))); } //----------------------------------------------------------------------- Region const *TangibleObject::getPvpRegion() const { uint32 crc = m_pvpRegionCrc.get(); if (crc) return Region::findRegionByNameCrc(crc); return 0; } //----------------------------------------------------------------------- /** * Sets the pvp region this object is in. * * @param pvpRegion the pvp region */ void TangibleObject::setPvpRegion(Region const * pvpRegion) { if (isAuthoritative()) { uint32 crc = pvpRegion ? pvpRegion->getNameCrc() : 0; if (m_pvpRegionCrc.get() != crc) m_pvpRegionCrc = crc; } else { FATAL(true, ("TangibleObject::setPvpRegion : send message to auth object!!!")); } } //----------------------------------------------------------------------- /** * Creates a default controller for this object. * * @return the object's controller */ Controller* TangibleObject::createDefaultController(void) { Controller* controller = new TangibleController(this); setController(controller); return controller; } // TangibleObject::createDefaultController //----------------------------------------------------------------------- void TangibleObject::forwardServerObjectSpecificBaselines() const { ServerObject::forwardServerObjectSpecificBaselines(); } //----------------------------------------------------------------------- TangibleObject const &TangibleObject::getPvpViewer(Client const &client) const { TangibleObject const * const clientObj = safe_cast(client.getCharacterObject()); FATAL(!clientObj, ("Client trying to get pvp viewer with no character object?")); return *clientObj; } //----------------------------------------------------------------------- void TangibleObject::sendObjectSpecificBaselinesToClient(Client const &client) const { ServerObject::sendObjectSpecificBaselinesToClient(client); if (!isNonPvpObject()) { uint32 flags, factionId; Pvp::getClientVisibleStatus(client, *this, flags, factionId); UpdatePvpStatusMessage const statusMessage(getNetworkId(), flags, factionId); client.send(statusMessage, true); PvpUpdateObserver::updatePvpStatusCache(&client, *this, flags, factionId); } } //----------------------------------------------------------------------- /** * Flags the object as being cached on the client or not. */ void TangibleObject::setCacheVersion(const int cacheVersion) { ServerObject::setCacheVersion(cacheVersion); // if we are cached, we need to be made invulnerable // @todo: if we are not cached, do we need to change our invulnerability? if (cacheVersion > 0) setInvulnerable(true); } // TangibleObject::setCacheVersion //----------------------------------------------------------------------- /** * Allows an object to do pre-deletion cleanup. */ void TangibleObject::onRemovingFromWorld() { ServerObject::onRemovingFromWorld(); clearHateList(); } // ---------------------------------------------------------------------- void TangibleObject::unload() { ServerObject::unload(); } // ---------------------------------------------------------------------- void TangibleObject::onPermanentlyDestroyed() { ServerObject::onPermanentlyDestroyed(); if (isAuthoritative()) { clearHateList(); if (isCraftingTool()) { ServerObject * owner = ServerWorld::findObjectByNetworkId(getOwnerId()); if (owner != NULL && owner->asCreatureObject() != NULL) { PlayerObject * player = PlayerCreatureController::getPlayerObject( owner->asCreatureObject()); if (player != NULL && player->isCrafting() && player->getCraftingTool() == getNetworkId()) player->stopCrafting(false); } } } if (isWaypoint()) { ServerPathfindingNotification::destroyWaypoint(this); } } //----------------------------------------------------------------------- /** * Creates a synchronized ui class in order to get data to the client during a * crafting session. * * @return the newly created class */ ServerSynchronizedUi * TangibleObject::createSynchronizedUi () { return new CraftingToolSyncUi(*this); } // ====================================================================== // TangibleObject private static member functions // ====================================================================== void TangibleObject::customizationDataModificationCallback(const CustomizationData & /* customizationData */, const void *context) { if (!context) return; //-- turn context into TangibleObject TangibleObject *const tangibleObject = const_cast(reinterpret_cast(context)); //-- set the tangible object's customization data modification flag now. // The reason we don't handle the customization data change now is that // we want to lump up all the changes that occur during one server // frame and handle the string conversion once. tangibleObject->setCustomizationDataModified(true); } // ====================================================================== // TangibleObject private member functions // ====================================================================== void TangibleObject::handleContainerSetup() { } //----------------------------------------------------------------------- void TangibleObject::setOwnerId(NetworkId const &id) { if(isAuthoritative()) { // if we are changing owners, we are no longer insured bool ownerChanged = false; if (m_ownerId.get() != id) { ownerChanged = true; setInsured(false); } m_ownerId = id; Container* container = ContainerInterface::getContainer(*this); if (container) { ContainerIterator i = container->begin(); for (; i != container->end(); ++i) { ServerObject* content = safe_cast((*i).getObject()); if (content && !dynamic_cast(content)) { content->setOwnerId(id); } } } // if we're hidden, who can see us has changed if (ownerChanged && isInWorld() && isHidden()) visibilityDataModified(); } else { sendControllerMessageToAuthServer(CM_setOwnerId, new MessageQueueNetworkId(id)); } } //----------------------------------------------------------------------- bool TangibleObject::onContainerAboutToTransfer(ServerObject * destination, ServerObject* transferer) { const int got = getGameObjectType (); if (got == SharedObjectTemplate::GOT_misc_crafting_station && destination && !destination->asCellObject()) { //Only allow stations to be trasferred if their hoppers are empty Container* container = ContainerInterface::getContainer(*this); if (container) { ContainerIterator i = container->begin(); for (; i != container->end(); ++i) { ServerObject* content = safe_cast((*i).getObject()); if (!isEmpty(*content)) { if (transferer && transferer->getClient()) ContainerInterface::sendContainerMessageToClient(*transferer, Container::CEC_HopperNotEmpty); return false; } } } } if (destination != NULL && getObjVars().hasItem(OBJVAR_BIO_LINK_ID)) { // if this item is bio-linked and a player is trying to equip it, make // sure the link id matches the player's id if (destination->asCreatureObject() != NULL) { // allow holograms to have biolinked items transferred to them int hologramVal = 0; destination->getObjVars().getItem("hologram_performer", hologramVal); NetworkId linkId(getBioLink()); if (linkId != destination->getNetworkId() && hologramVal == 0) { if (transferer && transferer->getClient()) ContainerInterface::sendContainerMessageToClient(*transferer, Container::CEC_BioLinkedToOtherPlayer); return false; } } // Check to see if we're trying to appearance equip a biolinked item. Object * containedBy = ContainerInterface::getContainedByObject(*destination); if(containedBy && containedBy->asServerObject() && containedBy->asServerObject()->asCreatureObject()) { CreatureObject * creatureContainer = containedBy->asServerObject()->asCreatureObject(); if(creatureContainer && creatureContainer->getAppearanceInventory() && creatureContainer->getAppearanceInventory() == destination) { // We're trying to appearance equip a biolinked item. Make sure the BioLink ID matches our player ID. NetworkId linkId(getBioLink()); if (linkId != creatureContainer->getNetworkId()) { if (transferer && transferer->getClient()) ContainerInterface::sendContainerMessageToClient(*transferer, Container::CEC_BioLinkedToOtherPlayer); return false; } } } } return ServerObject::onContainerAboutToTransfer(destination, transferer); } //----------------------------------------------------------------------- bool TangibleObject::onContainerChildAboutToLoseItem(ServerObject *destination, ServerObject &item, ServerObject *transferer) { int const got = getGameObjectType(); if (transferer && got == SharedObjectTemplate::GOT_misc_crafting_station && !isInWorld()) return false; if (getPortalProperty() && !onPobChildAboutToLoseItem(destination, item, transferer)) return false; return ServerObject::onContainerChildAboutToLoseItem(destination, item, transferer); } //----------------------------------------------------------------------- bool TangibleObject::onContainerChildAboutToGainItem(ServerObject &item, ServerObject *destination, ServerObject *transferer) { int const got = getGameObjectType(); if (transferer && got == SharedObjectTemplate::GOT_misc_crafting_station && !isInWorld()) return false; if (getPortalProperty() && !onPobChildAboutToGainItem(item, destination, transferer)) return false; return ServerObject::onContainerChildAboutToGainItem(item, destination, transferer); } // ---------------------------------------------------------------------- void TangibleObject::onContainerChildLostItem(ServerObject *destination, ServerObject &item, ServerObject *source, ServerObject *transferer) { if (getPortalProperty()) onPobChildLostItem(destination, item, source, transferer); ServerObject::onContainerChildLostItem(destination, item, source, transferer); } // ---------------------------------------------------------------------- void TangibleObject::onContainerChildGainItem(ServerObject &item, ServerObject *source, ServerObject *transferer) { if (getPortalProperty()) onPobChildGainItem(item, source, transferer); ServerObject::onContainerChildGainItem(item, source, transferer); } // ---------------------------------------------------------------------- int TangibleObject::onContainerAboutToGainItem(ServerObject& item, ServerObject* transferer) { if (transferer && isCraftingTool()) { return Container::CEC_BlockedByDestinationContainer; } return ServerObject::onContainerAboutToGainItem(item, transferer); } //---------------------------------------------------------------------- void TangibleObject::onContainerGainItem(ServerObject& item, ServerObject* source, ServerObject* transferer) { ServerObject::onContainerGainItem(item, source, transferer); //do not set owners of creatures //and do not set the owner of items going into the bazaar if (getOwnerId() != NetworkId::cms_invalid && !item.asCreatureObject() && !isBazaarTerminal()) item.setOwnerId(getOwnerId()); } // ---------------------------------------------------------------------- Footprint *TangibleObject::getFootprint() { CollisionProperty *const property = getCollisionProperty(); if (property) return property->getFootprint(); else return NULL; } //----------------------------------------------------------------------- Footprint const *TangibleObject::getFootprint() const { CollisionProperty const *const property = getCollisionProperty(); if (property) return property->getFootprint(); else return NULL; } // ---------------------------------------------------------------------- /** * Sets up the object for being in or out of combat. * * @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 TangibleObject::setInCombat(bool inCombat) { m_inCombat = inCombat; } //----------------------------------------------------------------------- /** * If this object is in combat, adds an aim to it. */ void TangibleObject::addAim(void) { if(isAuthoritative()) { if (isInCombat()) { CombatEngineData::AttackData & attackData = NON_NULL(getCombatData())->attackData; if (attackData.aims < ConfigCombatEngine::getMaxAims()) { ++attackData.aims; } } } else { sendControllerMessageToAuthServer(CM_addAim, 0); } } // TangibleObject::addAim //----------------------------------------------------------------------- /** * If this object is in combat, clear's it's aims. */ void TangibleObject::clearAims(void) { if(isAuthoritative()) { if (isInCombat()) { CombatEngineData::AttackData & attackData = NON_NULL(getCombatData())->attackData; attackData.aims = 0; } } else { sendControllerMessageToAuthServer(CM_clearAims, 0); } } // TangibleObject::clearAims //----------------------------------------------------------------------- /** * Applies damage to the object. No other function should modify m_damageTaken. * * @param delta amount of damage to apply * @param ignoreInvulnerable flag to ignore the invulnerable setting of this object * @param source who is doing the damage * * @return the amount of damage actually applied */ int TangibleObject::alterHitPoints(int delta, bool ignoreInvulnerable, const NetworkId & source) { if(isAuthoritative()) { #ifdef _DEBUG char debugBuffer[1024]; const char * craftedString = ""; Client *client = NULL; ServerObject * sourceObject = safe_cast(NetworkIdManager::getObjectById(source)); if (source != NetworkId::cms_invalid && sourceObject != NULL) client = sourceObject->getClient(); #endif // Only consider tangible invulnerability rules for this case if (isDisabled() || (!ignoreInvulnerable && TangibleObject::isInvulnerable())) return 0; int totalHitPoints = getMaxHitPoints(); int totalDamageTaken = m_damageTaken.get() - delta; #ifdef _DEBUG if (ConfigCombatEngine::getDebugDamage()) { snprintf(debugBuffer, sizeof(debugBuffer), "%s object %s (hp=%d) damaged " "from %d to %d\n", craftedString, getNetworkId().getValueString().c_str(), totalHitPoints, m_damageTaken.get(), totalDamageTaken); DEBUG_REPORT_LOG (true, ("%s", debugBuffer)); if (source != NetworkId::cms_invalid && client != NULL) ConsoleMgr::broadcastString(debugBuffer, client); } #endif if (totalDamageTaken >= totalHitPoints) { // clip delta and damage taken if (isDisabled()) delta = 0; else delta += totalDamageTaken - totalHitPoints; totalDamageTaken = totalHitPoints; } else if (totalDamageTaken < 0) totalDamageTaken = 0; if (totalDamageTaken == totalHitPoints) setDisabled(true); m_damageTaken = totalDamageTaken; if (isDisabled()) { #ifdef _DEBUG if (ConfigCombatEngine::getDebugDamage()) { snprintf(debugBuffer, sizeof(debugBuffer), "%s object %s has been " "disabled\n", craftedString, getNetworkId().getValueString().c_str()); DEBUG_REPORT_LOG (true, ("%s", debugBuffer)); if (source != NetworkId::cms_invalid && client != NULL) ConsoleMgr::broadcastString(debugBuffer, client); } #endif ScriptParams params; params.addParam(source); IGNORE_RETURN(getScriptObject()->trigAllScripts( Scripting::TRIG_OBJECT_DISABLED, params)); } scheduleForAlter(); return delta; } else { sendControllerMessageToAuthServer(CM_alterHitPoints, new MessageQueueGenericValueType >(std::make_pair(delta, source))); return 0; } } // TangibleObject::alterHitpoints //----------------------------------------------------------------------- /** * Applies damage done during combat to the object. * * @param damageData the damage */ void TangibleObject::applyDamage(const CombatEngineData::DamageData &damageData) { int totalDamage = 0; WARNING_STRICT_FATAL(! isAuthoritative(), ("TangibleObject::applyDamage() invoked on a NON-AUTHORITATIVE object %s:%s.", getObjectTemplateName(), getNetworkId().getValueString().c_str())); // it is possible for damageData to go bad if this object is being damaged from // combat and it goes out of combat, so we copy the items we need off of it const NetworkId attackerId(damageData.attackerId); const NetworkId weaponId(damageData.weaponId); std::vector::const_iterator iter; for (iter = damageData.damage.begin(); !isDisabled() && iter != damageData.damage.end(); ++iter) { const AttribMod::AttribMod &mod = *iter; if (mod.value == 0) continue; if (AttribMod::isDirectDamage(mod)) { // apply instantanious damage directly totalDamage -= alterHitPoints(mod.value, damageData.ignoreInvulnerable, attackerId); } else { WARNING(true, ("Trying to apply non-immediate damage to tangible " "object %s. This isn't supported, the damage won't be applied", getNetworkId().getValueString().c_str())); } } // tell scripts we have taken combat damage if (attackerId != NetworkId::cms_invalid && totalDamage > 0) { NOT_NULL(getScriptObject()); ScriptParams params; params.addParam(attackerId); params.addParam(weaponId); params.addParam(totalDamage); IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_OBJECT_DAMAGED, params)); } } // ---------------------------------------------------------------------- /** * Sends a message to the Planet Server to update the position of the object. */ void TangibleObject::updatePlanetServerInternal(const bool forceUpdate) const { // Don't send updates for contained tangibles without an interest radius, and only send when forced or moving if ((forceUpdate || getPositionChanged()) && (!ContainerInterface::getContainedByObject(*this) || getInterestRadius() > 0)) { Object const * const topmostContainer = ContainerInterface::getTopmostContainer(*this); FATAL(!topmostContainer, ("Object %s was contained by something, but getTopmostContainer() returned NULL\n",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), getInterestRadius(), static_cast (getLocationReservationRadius()), false, false, static_cast(getObjectType()), 0, false, getTemplateCrc(), -1, -1); GameServer::getInstance().sendToPlanetServer(msg); } } //---------------------------------------------------------------------- /** * Clears our damage list. */ void TangibleObject::clearDamageList(void) { if (m_combatData != NULL) { m_combatData->defenseData.damage.clear(); } } //---------------------------------------------------------------------- bool TangibleObject::addHate(NetworkId const & target, float const hate) { bool result = false; if (isAuthoritative()) { if (m_hateList.addHate(target, hate)) { result = true; CombatTracker::addDefender(this); setInCombat(true); scheduleForAlter(); } } else { sendControllerMessageToAuthServer(CM_addHate, new MessageQueueGenericValueType >(std::make_pair(target, hate))); } return result; } //---------------------------------------------------------------------- void TangibleObject::addHateOverTime(NetworkId const & target, float const hate, int const seconds) { if (seconds <= 0) return; if (isAuthoritative()) { unsigned long const currentGameTime = ServerClock::getInstance().getGameTimeSeconds(); unsigned long const endGameTime = currentGameTime + seconds; IGNORE_RETURN(addHate(target, hate)); bool const firstHateOverTime = m_hateOverTime.empty(); m_hateOverTime.set(target, std::make_pair(hate, std::make_pair(currentGameTime, endGameTime))); if (firstHateOverTime) MessageToQueue::sendMessageToC(getNetworkId(), "C++ProcessHateOverTime", std::string(), 1, false); } else { sendControllerMessageToAuthServer(CM_addHateOverTime, new MessageQueueGenericValueType > >(std::make_pair(target, std::make_pair(seconds, hate)))); } } //---------------------------------------------------------------------- bool TangibleObject::setHate(NetworkId const & target, float const hate) { bool result = false; if (isAuthoritative()) { if (m_hateList.setHate(target, hate)) { result = true; CombatTracker::addDefender(this); setInCombat(true); scheduleForAlter(); } } else { sendControllerMessageToAuthServer(CM_setHate, new MessageQueueGenericValueType >(std::make_pair(target, hate))); } return result; } //---------------------------------------------------------------------- float TangibleObject::getHate(NetworkId const & target) const { return m_hateList.getHate(target); } //---------------------------------------------------------------------- float TangibleObject::getMaxHate() const { return m_hateList.getMaxHate(); } //---------------------------------------------------------------------- CachedNetworkId const & TangibleObject::getHateTarget() const { return m_hateList.getTarget(); } //---------------------------------------------------------------------- void TangibleObject::removeHateTarget(NetworkId const & target) { if (isAuthoritative()) { m_hateList.removeTarget(target); scheduleForAlter(); } else { sendControllerMessageToAuthServer(CM_removeHateTarget, new MessageQueueGenericValueType(target)); } } //---------------------------------------------------------------------- void TangibleObject::clearHateList() { if (isAuthoritative()) { m_hateList.clear(); scheduleForAlter(); } else { sendControllerMessageToAuthServer(CM_clearHateList, NULL); } } //----------------------------------------------------------------------- HateList::UnSortedList const & TangibleObject::getUnSortedHateList() const { return m_hateList.getUnSortedList(); } //----------------------------------------------------------------------- void TangibleObject::getSortedHateList(HateList::SortedList & sortedHateList) const { m_hateList.getSortedList(sortedHateList); } //----------------------------------------------------------------------- void TangibleObject::verifyHateList() { LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", ("TangibleObject::verifyHateList() owner(%s:%s)", getNetworkId().getValueString().c_str(), Unicode::wideToNarrow(getEncodedObjectName()).c_str())); m_hateList.alter(); } //----------------------------------------------------------------------- bool TangibleObject::isHatedBy(Object * const object) { bool result = false; TangibleObject * const hatedByTangibleObject = TangibleObject::asTangibleObject(object); if (hatedByTangibleObject != NULL) { HateList::UnSortedList const & hateList = hatedByTangibleObject->getUnSortedHateList(); if (hateList.find(CachedNetworkId(*this)) != hateList.end()) { result = true; } } return result; } //----------------------------------------------------------------------- bool TangibleObject::isOnHateList(NetworkId const & target) const { return m_hateList.isOnList(target); } //----------------------------------------------------------------------- int TangibleObject::getTimeSinceLastHateListUpdate() const { return m_hateList.getTimeSinceLastUpdate(); } //----------------------------------------------------------------------- bool TangibleObject::isHateListEmpty() const { return m_hateList.isEmpty(); } //----------------------------------------------------------------------- HateList::RecentList const & TangibleObject::getRecentHateList() const { return m_hateList.getRecentList(); } //----------------------------------------------------------------------- void TangibleObject::clearRecentHateList() { m_hateList.clearRecentList(); scheduleForAlter(); } //----------------------------------------------------------------------- void TangibleObject::resetHateTimer() { if (isAuthoritative()) { m_hateList.resetHateTimer(); scheduleForAlter(); } else { sendControllerMessageToAuthServer(CM_resetHateTimer, NULL); } } //----------------------------------------------------------------------- void TangibleObject::setHateListAutoExpireTargetEnabled(bool const enabled) { m_hateList.setAutoExpireTargetEnabled(enabled); if (!isAuthoritative()) { sendControllerMessageToAuthServer(CM_autoExpireHateListTargetEnabled, new MessageQueueGenericValueType(enabled)); } } //----------------------------------------------------------------------- bool TangibleObject::isHateListAutoExpireTargetEnabled() const { return m_hateList.isAutoExpireTargetEnabled(); } //----------------------------------------------------------------------- int TangibleObject::getHateListAutoExpireTargetDuration() const { return m_hateList.getAutoExpireTargetDuration(); } //----------------------------------------------------------------------- float TangibleObject::getAILeashTime() const { return m_hateList.getAILeashTime(); } //----------------------------------------------------------------------- void TangibleObject::setAILeashTime( float time ) { m_hateList.setAILeashTime( time ); } //----------------------------------------------------------------------- /** * @return true if we are crafted, false if not */ bool TangibleObject::isCrafted(void) const { return hasCondition(C_crafted); } // TangibleObject::isCrafted //----------------------------------------------------------------------- /** * Returns the id of the manufacturing schematic that was used to craft this * item, or NetworkId::cms_invalid if the item wasn't crafted. * * @return the schematic id */ const NetworkId TangibleObject::getCraftedId(void) const { NetworkId craftedId(NetworkId::cms_invalid); getObjVars().getItem(OBJVAR_CRAFTING_SCHEMATIC,craftedId); return craftedId; } // TangibleObject::getCraftedId //----------------------------------------------------------------------- /** * Sets the id of the manufacturing schematic used to create this object. * * @param id the schematic id */ void TangibleObject::setCraftedId(const NetworkId & id) { if (isAuthoritative()) { if (id == NetworkId::cms_invalid) { IGNORE_RETURN(removeObjVarItem(OBJVAR_CRAFTING_SCHEMATIC)); m_sourceDraftSchematic = 0; clearCondition(C_crafted); } else { IGNORE_RETURN(setObjVarItem(OBJVAR_CRAFTING_SCHEMATIC, id)); setCondition(C_crafted); const Object * object = NetworkIdManager::getObjectById(id); if (object != NULL) { const ManufactureSchematicObject * schematic = dynamic_cast< const ManufactureSchematicObject *>(object); if (schematic != NULL) m_sourceDraftSchematic = schematic->getDraftSchematic(); } } } else { sendControllerMessageToAuthServer(CM_setCraftedId, new MessageQueueGenericValueType(id)); } } // TangibleObject::setCraftedId //----------------------------------------------------------------------- /** * Returns the crafting type of a crafting/repair tool. * * @return the crafting type */ int TangibleObject::getCraftingType(void) const { int type = 0; getObjVars().getItem(OBJVAR_CRAFTING_TYPE, type); return type; } // TangibleObject::getCraftingType //----------------------------------------------------------------------- /** * Returns the crafted type of a crafted object. * * @return the crafted type */ int TangibleObject::getCraftedType(void) const { int craftedType = 0; getObjVars().getItem(OBJVAR_CRAFTING_REPAIR_TYPE,craftedType); return craftedType; } // TangibleObject::getCraftedType //----------------------------------------------------------------------- /** * Sets the crafted type of a crafted object. * * @param type the crafted type */ void TangibleObject::setCraftedType(int type) { if (isAuthoritative()) { IGNORE_RETURN(setObjVarItem(OBJVAR_CRAFTING_REPAIR_TYPE, type)); } else { sendControllerMessageToAuthServer(CM_setCraftedType, new MessageQueueGenericValueType(type)); } } // TangibleObject::setCraftedType //----------------------------------------------------------------------- /** * Returns the id of the object's creator's id. * * @return the object's creator's id */ const NetworkId TangibleObject::getCreatorId(void) const { if (m_creatorId.get() == NetworkId::cms_invalid) { // @todo remove this code when no object has the objvar // OBJVAR_CREATOR_ID NetworkId creatorId(NetworkId::cms_invalid); if (getObjVars().getItem(OBJVAR_CREATOR_ID, creatorId)) { const_cast(this)->setCreatorId(creatorId); return creatorId; } } return m_creatorId.get(); } // TangibleObject::getCreatorId //----------------------------------------------------------------------- /** * Sets the creator id of this object. * * @param creatorId the creator id */ void TangibleObject::setCreatorId(const NetworkId & creatorId) { if (isAuthoritative()) { m_creatorId = creatorId; if (getObjVars().hasItem(OBJVAR_CREATOR_ID)) removeObjVarItem(OBJVAR_CREATOR_ID); } else { sendControllerMessageToAuthServer(CM_setCreatorId, new MessageQueueGenericValueType(creatorId)); } } // TangibleObject::setCreatorId //----------------------------------------------------------------------- /** * Returns the id of the object's creator's name. * * @return the object's creator's name */ const Unicode::String TangibleObject::getCreatorName(void) const { return Unicode::narrowToWide(NameManager::getInstance().getPlayerName( getCreatorId())); } // TangibleObject::getCreatorName //----------------------------------------------------------------------- /** * Returns the xp amount the creator of this object will get when it is "used". * * @return the xp amount */ int TangibleObject::getCreatorXp(void) const { int creatorXp=0; getObjVars().getItem(OBJVAR_CREATOR_XP,creatorXp); return creatorXp; } // TangibleObject::getCreatorXp //----------------------------------------------------------------------- /** * Sets the xp amount the creator of this object will get when it is "used". * * @param xp the xp amount */ void TangibleObject::setCreatorXp(int xp) { if (isAuthoritative()) { setObjVarItem(OBJVAR_CREATOR_XP, xp); } else { sendControllerMessageToAuthServer(CM_setCreatorXp, new MessageQueueGenericValueType(xp)); } } // TangibleObject::setCreatorXp //----------------------------------------------------------------------- /** * Returns the xp type the creator of this object will get when it is "used". * * @return the xp type */ int TangibleObject::getCreatorXpType(void) const { int creatorXpType = -1; getObjVars().getItem(OBJVAR_CREATOR_XP_TYPE,creatorXpType); return creatorXpType; } // TangibleObject::getCreatorXpType //----------------------------------------------------------------------- /** * Sets the xp type the creator of this object will get when it is "used". * * @param type the xp type */ void TangibleObject::setCreatorXpType(int type) { if (isAuthoritative()) { setObjVarItem(OBJVAR_CREATOR_XP_TYPE, type); } else { sendControllerMessageToAuthServer(CM_setCreatorXpType, new MessageQueueGenericValueType(type)); } } // TangibleObject::setCreatorXpType //----------------------------------------------------------------------- /** * Returns if this object is a crafting tool or not. * * @return true if we are a tool, false if not */ bool TangibleObject::isCraftingTool(void) const { return getObjVars().hasItem(OBJVAR_CRAFTING_TOOL); } // TangibleObject::isCraftingTool //----------------------------------------------------------------------- /** * Returns if this object is a crafting station or not. * * @return true if we are a station, false if not */ bool TangibleObject::isCraftingStation(void) const { return getObjVars().hasItem(OBJVAR_CRAFTING_STATION); } // TangibleObject::isCraftingStation //----------------------------------------------------------------------- /** * Returns if this object is a repair tool or not. * * @return true if we are a repair tool, false if not */ bool TangibleObject::isRepairTool(void) const { return getObjVars().hasItem(OBJVAR_REPAIR_TOOL); } // TangibleObject::isRepairTool //----------------------------------------------------------------------- /** * If this is a crafting tool, tries to start a session with a player. * * @param crafter the player who wants to craft something * * @return true if the session was started, false if not */ bool TangibleObject::startCraftingSession(CreatureObject & crafter) { static const SlotId outputSlotId(SlotIdManager::findSlotId(MANF_OUTPUT_SLOT_NAME)); if(isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode PlayerObject * crafterPlayer = PlayerCreatureController::getPlayerObject(&crafter); if (crafterPlayer == NULL) return false; // make sure we are a crafting tool if (!isCraftingTool()) { DEBUG_WARNING(true, ("Tried to start crafting session on non-tool " "object %s", myIdString)); return false; } if (isDisabled()) { DEBUG_WARNING(true, ("Tried to start crafting session on disabled " "tool %s", myIdString)); return false; } // make sure the tool isn't creating a prototype float realTimeVar = 0; getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_TIME,realTimeVar); if (realTimeVar > 0) { StringId message("system_msg", "crafting_tool_creating_prototype"); Unicode::String outOfBand; Chat::sendSystemMessage(crafter, message, outOfBand); return false; } // make sure the tool isn't already being used if (getObjVars().hasItem(OBJVAR_CRAFTING_CRAFTER)) { StringId message("system_msg", "crafting_tool_in_use"); Unicode::String outOfBand; Chat::sendSystemMessage(crafter, message, outOfBand); return false; } // make sure the output slot is empty SlottedContainer const * const slotContainer = ContainerInterface::getSlottedContainer(*this); if (slotContainer == NULL) { DEBUG_WARNING(true, ("Crafting tool %s does not have a container!", myIdString)); return false; } Container::ContainerErrorCode tmp = Container::CEC_Success; if (slotContainer->getObjectInSlot(outputSlotId, tmp) != Container::ContainedItem::cms_invalid) { StringId message("system_msg", "crafting_tool_full"); Unicode::String outOfBand; Chat::sendSystemMessage(crafter, message, outOfBand); return false; } // get the schematics list from the player and filter for the type of // schematics we can make int myType = getCraftingType(); bool sendComplexities = true; if (!crafter.isSchematicFilteringEnabled()) sendComplexities = false; std::vector goodSchematics; std::vector scriptComplexities; const std::map,int> & schematics = crafter.getDraftSchematics(); std::map,int>::const_iterator iter; for (iter = schematics.begin(); iter != schematics.end(); ++iter) { const DraftSchematicObject * schematic = DraftSchematicObject::getSchematic((*iter).first.first); if (schematic != NULL && ((myType & ServerObjectTemplate::CT_genericItem) != 0 || ((schematic->getCategory() & myType) != 0))) { // make sure the player has all the skill commands needed for the // schematic int skillCount = schematic->getSkillCommandsCount(); int i; for (i = 0; i < skillCount; ++i) { if (!crafter.hasCommand(schematic->getSkillCommands(i))) break; } if (i == skillCount) { goodSchematics.push_back((*iter).first.first); if (sendComplexities) scriptComplexities.push_back(schematic->getComplexity()); else scriptComplexities.push_back(1.0f); } } } crafterPlayer->setCraftingTool(*this); if (goodSchematics.size() > 0) { ScriptParams params; params.addParam(crafter.getNetworkId()); params.addParam(goodSchematics); params.addParam(scriptComplexities); if (getScriptObject()->trigAllScripts(Scripting::TRIG_REQUEST_DRAFT_SCHEMATICS, params) != SCRIPT_CONTINUE) { std::vector empty; crafterPlayer->sendUseableDraftSchematics (empty); crafterPlayer->stopCrafting(true); return false; } setObjVarItem(OBJVAR_CRAFTING_CRAFTER, crafter.getNetworkId()); } else { std::vector empty; crafterPlayer->sendUseableDraftSchematics (empty); crafterPlayer->stopCrafting(true); StringId message("system_msg", "no_valid_schematics"); Unicode::String outOfBand; Chat::sendSystemMessage(crafter, message, outOfBand); return false; } // the following section to send the ingredients to the player is only needed for // testing crafting via the console #ifdef _DEBUG // send the player info about the ingredients in the tool's hopper; // the actual sending of the messages could be spread out over a few // frames // listIngredientsInHopper(crafter); #endif } else { sendControllerMessageToAuthServer(CM_startCraftingSession, new MessageQueueGenericValueType(crafter.getNetworkId())); } return true; } // TangibleObject::startCraftingSession //----------------------------------------------------------------------- /** * If this is a crafting tool, tries to stop a session with a player. * * @return true if the session was stopped, false if not */ bool TangibleObject::stopCraftingSession(void) { if (isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool()) { DEBUG_WARNING(true, ("Tried to stop crafting session on non-tool " "object %s", myIdString)); return false; } // make sure a prototype object isn't being made (the session will be // stopped when the prototype is done) float realTimeVar = 0; if (getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_TIME, realTimeVar) && realTimeVar > 0) return false; // make sure the tool isn't already being used NetworkId crafterId; if (!getObjVars().getItem(OBJVAR_CRAFTING_CRAFTER,crafterId)) { DEBUG_REPORT_LOG(true, ("Crafting tool %s in not in use\n", myIdString)); return false; } clearCraftingManufactureSchematic(); clearCraftingPrototype(); removeObjVarItem(OBJVAR_CRAFTING_CRAFTER); } else { sendControllerMessageToAuthServer(CM_stopCraftingSession, 0); } return true; } // TangibleObject::stopCraftingSession //----------------------------------------------------------------------- /** * Test to see if an object is in a crafting tool's ingredient hopper. * * @param ingredientId object we want to test * * @return true if the object is in the hopper, false if not */ bool TangibleObject::isIngredientInHopper(const NetworkId & ingredientId) const { ServerObject const * const hopper = getIngredientHopper(); if (hopper == NULL) return false; Object const * const ingredient = CachedNetworkId(ingredientId).getObject(); if (ingredient == NULL) return false; return ContainerInterface::isNestedWithin(*ingredient, getNetworkId()); } // TangibleObject::isIngredientInHopper //----------------------------------------------------------------------- /** * Returns this crafting station's ingredient hopper. * * @return the hopper */ ServerObject * TangibleObject::getIngredientHopper(void) const { static const SlotId hopperSlotId(SlotIdManager::findSlotId(INGREDIENT_HOPPER_SLOT_NAME)); const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting station if (!isCraftingStation()) { DEBUG_WARNING(true, ("Tried to get input hopper for non-crafting station " "object %s", myIdString)); return NULL; } // get the ingredient hopper SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); if (container == NULL) { DEBUG_WARNING(true, ("Crafting tool %s does not have a container!", myIdString)); return NULL; } Container::ContainerErrorCode tmp = Container::CEC_Success; Container::ContainedItem hopperId = container->getObjectInSlot(hopperSlotId, tmp); if (hopperId == NetworkId::cms_invalid) { DEBUG_WARNING(true, ("Crafting tool %s does not have a ingredient " "hopper!", myIdString)); return NULL; } ServerObject * hopper = safe_cast(hopperId.getObject()); if (hopper == NULL) { DEBUG_WARNING(true, ("Can't find object for ingredient hopper id %s", hopperId.getValueString().c_str())); return NULL; } return hopper; } // TangibleObject::getIngredientHopper //---------------------------------------------------------------------- /** * Adds an object to this tool's output slot. * * @param object the object to add * @param transferer who is transferring the ingredient * * @return true on success, false on fail */ bool TangibleObject::addObjectToOutputSlot(ServerObject & object, ServerObject * transferer) { static const SlotId outputSlotId(SlotIdManager::findSlotId(MANF_OUTPUT_SLOT_NAME)); if(isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // get output slot SlottedContainer const * const slotContainer = ContainerInterface::getSlottedContainer(*this); if (slotContainer == NULL) { DEBUG_WARNING(true, ("Crafting tool %s does not have a container!", myIdString)); return false; } // make sure the transferer is in the final stage of crafting if (transferer == NULL) { DEBUG_WARNING(true, ("addObjectToOutputSlot, NULL transferer")); return false; } NetworkId crafterVarId(NetworkId::cms_invalid); if (!getObjVars().getItem(OBJVAR_CRAFTING_CRAFTER, crafterVarId)) { // @todo: send a message to the player saying this tool is in use DEBUG_WARNING(true, ("addObjectToOutputSlot, crafting tool %s has " "no crafter", myIdString)); return false; } if (crafterVarId != transferer->getNetworkId()) { DEBUG_WARNING(true, ("addObjectToOutputSlot, transferer %s is not the " "crafter %s", transferer->getNetworkId().getValueString().c_str(), crafterVarId.getValueString().c_str())); return false; } CreatureObject const * const creatureTransferer = transferer->asCreatureObject(); if (creatureTransferer == NULL) { DEBUG_WARNING(true, ("addObjectToOutputSlot, transferer %s is not a " "creature", transferer->getNetworkId().getValueString().c_str())); return false; } PlayerObject const * const creaturePlayer = PlayerCreatureController::getPlayerObject(creatureTransferer); if (creaturePlayer == NULL) return false; if (creaturePlayer->getCraftingStage() != Crafting::CS_finish) { DEBUG_WARNING(true, ("addObjectToOutputSlot, crafter %s is in the " "wrong stage %d", transferer->getNetworkId().getValueString().c_str(), static_cast(creaturePlayer->getCraftingStage()))); return false; } // if the object is our prototype, clear the prototype if (getSynchronizedUi() != NULL) { CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync != NULL) { if (sync->getPrototype() == object.getNetworkId()) { sync->setPrototype(CachedNetworkId::cms_cachedInvalid); } } } // put the object in the slot Container::ContainerErrorCode tmp = Container::CEC_Success; if (!ContainerInterface::transferItemToSlottedContainer(*this, object, outputSlotId, NULL, tmp)) { DEBUG_WARNING(true, ("Failed to transfer object %s to crafting " "tool %s", object.getNetworkId().getValueString().c_str(), myIdString)); return false; } } else { NetworkId transfererId; if(transferer) transfererId = transferer->getNetworkId(); sendControllerMessageToAuthServer(CM_addObjectToOutputSlot, new MessageQueueGenericValueType >(std::make_pair(object.getNetworkId(), transfererId))); } return true; } // TangibleObject::addObjectToOutputSlot //---------------------------------------------------------------------- /** * Gets the temporary manufacturing schematic used during a crafting session. * * @return the schematic */ ManufactureSchematicObject * TangibleObject::getCraftingManufactureSchematic(void) const { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool()) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return NULL; } SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); if (container) { Container::ContainerErrorCode error; const CachedNetworkId & schematicId = container->getObjectInSlot(getCraftingManufactureSchematicSlotId(), error); return safe_cast(schematicId.getObject()); } else { DEBUG_WARNING(true, ("ManufactureSchematicObject doesn't have a slotted container.")); return NULL; } } // TangibleObject::getCraftingManufactureSchematic //---------------------------------------------------------------------- /** * Gives the schematic to the player at the end of the session. * * @return the schematic */ ManufactureSchematicObject * TangibleObject::removeCraftingManufactureSchematic(void) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool() || getSynchronizedUi() == NULL) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return NULL; } CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync == NULL) return NULL; ManufactureSchematicObject * schematic = safe_cast( sync->getManfSchematic().getObject()); sync->setManfSchematic(CachedNetworkId::cms_cachedInvalid, CachedNetworkId::cms_cachedInvalid, false); return schematic; } // TangibleObject::removeCraftingManufactureSchematic //---------------------------------------------------------------------- /** * Stores the temporary manufacturing schematic used during crafting in this * crafting tool. * * @param schematic the schematic */ void TangibleObject::setCraftingManufactureSchematic(ManufactureSchematicObject & schematic) { if(isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool() || getSynchronizedUi() == NULL) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return; } CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync == NULL) return; // see if there is already a schematic object set const CachedNetworkId & schematicId = sync->getManfSchematic(); if (schematicId == schematic.getNetworkId()) return; // remove the old schematic ServerObject * object = safe_cast(schematicId.getObject()); if (object == NULL) { if (schematicId != NetworkId::cms_invalid) { DEBUG_WARNING(true, ("Can't find object for schematic id %s", schematicId.getValueString().c_str())); // go on anyway } } else { sync->setManfSchematic(CachedNetworkId::cms_cachedInvalid, CachedNetworkId::cms_cachedInvalid, true); object->permanentlyDestroy(DeleteReasons::Consumed); } // the schematic isn't in the world or in a container, so we need to // tell the client about it int stationBonus = 0; ServerObject * crafter = NULL; NetworkId crafterId; if (getObjVars().getItem(OBJVAR_CRAFTING_CRAFTER,crafterId)) { crafter = safe_cast(NetworkIdManager::getObjectById(crafterId)); } if (crafter != NULL) { sync->setManfSchematic(CachedNetworkId(schematic), CachedNetworkId(*crafter), true); // if the crafter is near a crafting station, get any crafting bonus // it has const PlayerObject * player = PlayerCreatureController::getPlayerObject( crafter->asCreatureObject()); if (player != NULL && player->getCraftingStation().getObject() != NULL) { player->getCraftingStation().getObject()->asServerObject()-> getObjVars().getItem(OBJVAR_CRAFTING_STATIONMOD, stationBonus); } } else { WARNING_STRICT_FATAL(true, ("TangibleObject::setCraftingManufactureSchematic " "no crafter id on crafting tool %s!", myIdString)); } // set any crafting bonus we give on the schematic int myBonus = 0; getObjVars().getItem(OBJVAR_CRAFTING_STATIONMOD, myBonus); if (myBonus + stationBonus != 0) { schematic.setObjVarItem(OBJVAR_CRAFTING_STATIONMOD, myBonus + stationBonus); } } else { sendControllerMessageToAuthServer(CM_setCraftingManufactureSchematic, new MessageQueueGenericValueType(schematic.getNetworkId())); } } // TangibleObject::setCraftingManufactureSchematic //---------------------------------------------------------------------- /** * Removes the temporary manufacturing schematic from the crafting tool and * deletes it. */ void TangibleObject::clearCraftingManufactureSchematic(void) { if(isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool()) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return; } // see if we have a schematic in our schematic slot CachedNetworkId schematicId = CachedNetworkId::cms_cachedInvalid; SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); if (container) { Container::ContainerErrorCode error = Container::CEC_Success; schematicId = container->getObjectInSlot( getCraftingManufactureSchematicSlotId(), error); } else { DEBUG_WARNING(true, ("failed to get slotted container for schematic lookup.")); } // get our ui CraftingToolSyncUi * sync = NULL; if (getSynchronizedUi() != NULL) sync = dynamic_cast(getSynchronizedUi()); if (schematicId == CachedNetworkId::cms_cachedInvalid && sync == NULL) { // no schematic return; } if (schematicId == CachedNetworkId::cms_invalid) schematicId = sync->getManfSchematic(); else if (sync != NULL && sync->getManfSchematic() != CachedNetworkId::cms_invalid && schematicId != sync->getManfSchematic()) { WARNING(true, ("TangibleObject::clearCraftingManufactureSchematic " "crafting tool %s has schematic %s in its slot but schematic %s " "in its ui!!!", getNetworkId().getValueString().c_str(), schematicId.getValueString().c_str(), sync->getManfSchematic().getValueString().c_str())); if (schematicId.getObject() != NULL) { schematicId.getObject()->asServerObject()->permanentlyDestroy( DeleteReasons::Consumed); } else { WARNING_STRICT_FATAL(true, ("TangibleObject::clearCraftingManufactureSchematic " "crafting tool %s has slotted schematic %s with no object!", getNetworkId().getValueString().c_str(), schematicId.getValueString().c_str())); } schematicId = sync->getManfSchematic(); } // get the schematic ManufactureSchematicObject * manfSchematic = NULL; ServerObject * object = safe_cast(schematicId.getObject()); if (object == NULL && schematicId != CachedNetworkId::cms_invalid) { WARNING_STRICT_FATAL(true, ("Can't find object for manufactring schematic " "id %s", schematicId.getValueString().c_str())); } else manfSchematic = safe_cast(object); if (manfSchematic != NULL) { NetworkId crafterId; if (getObjVars().getItem(OBJVAR_CRAFTING_CRAFTER,crafterId)) { // if we are at the assembly stage of crafting, or the draft schematic says // not to destroy the ingredients, put the ingredients in the schematic back // in the tool CreatureObject * const crafter = dynamic_cast(NetworkIdManager::getObjectById(crafterId)); PlayerObject * const crafterPlayer = PlayerCreatureController::getPlayerObject(crafter); if (!manfSchematic->mustDestroyIngredients() || (crafterPlayer != NULL && crafterPlayer->getCraftingStage() == Crafting::CS_assembly)) { crafterPlayer->setAllowEmptySlot(true); int count = manfSchematic->getSlotsCount(); // note this loop is odd because when we clear a slot it may // actually be removed if it was optional Crafting::IngredientSlot manfSlot; for (int i = 0; i < count;) { if (manfSchematic->getSlot(i, manfSlot, false)) { if (manfSlot.ingredientType != Crafting::IT_none && !manfSlot.ingredients.empty()) { if (crafterPlayer->emptySlot(i, NetworkId::cms_invalid)) { if (count != manfSchematic->getSlotsCount()) { count = manfSchematic->getSlotsCount(); continue; } } } } ++i; } crafterPlayer->setAllowEmptySlot(false); } else manfSchematic->destroyAllIngredients(); } } if (sync != NULL) sync->setManfSchematic(CachedNetworkId::cms_cachedInvalid, CachedNetworkId::cms_cachedInvalid, true); if (object != NULL) { object->permanentlyDestroy(DeleteReasons::Consumed); } } else { sendControllerMessageToAuthServer(CM_clearCraftingManufactureSchematic, 0); } } // TangibleObject::clearCraftingManufactureSchematic //---------------------------------------------------------------------- /** * Gets the temporary prototype used during a crafting session. May also be used * to give the prototype to the player at the end of the session. * * @return the prototype */ ServerObject * TangibleObject::getCraftingPrototype(void) const { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool() || getSynchronizedUi() == NULL) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return NULL; } const CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync == NULL) return NULL; return safe_cast(sync->getPrototype().getObject()); } // TangibleObject::getCraftingPrototype //---------------------------------------------------------------------- /** * Stores the temporary prototype object used during crafting in this * crafting tool. * * @param prototype the prototype */ void TangibleObject::setCraftingPrototype(ServerObject & prototype) { if (isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool() || getSynchronizedUi() == NULL) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return; } CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync == NULL) return; // see if there is already a prototype object set const CachedNetworkId & prototypeId = sync->getPrototype(); if (prototypeId == prototype.getNetworkId()) return; // remove the old prototype ServerObject * object = safe_cast(prototypeId.getObject()); if (object == NULL) { if (prototypeId != NetworkId::cms_invalid) { DEBUG_WARNING(true, ("Can't find object for prototype id %s", prototypeId.getValueString().c_str())); // go on anyway } } else { sync->setPrototype(CachedNetworkId::cms_cachedInvalid); object->permanentlyDestroy(DeleteReasons::Consumed); } // put the prototype in the tool's slot so the crafter will get updates // about it Container::ContainerErrorCode tmp = Container::CEC_Success; if (!ContainerInterface::transferItemToSlottedContainer(*this, prototype, getCraftingPrototypeSlotId(), NULL, tmp)) { // see if there is something in the slot, and delete it if there is bool failed = true; SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); NOT_NULL(container); const Container::ContainedItem & oldItem = container->getObjectInSlot( getCraftingPrototypeSlotId(), tmp); if (oldItem != prototype.getNetworkId()) { if (oldItem.getObject() != NULL) { safe_cast(oldItem.getObject())->permanentlyDestroy( DeleteReasons::Consumed); // try the transfer again if (ContainerInterface::transferItemToSlottedContainer(*this, prototype, getCraftingPrototypeSlotId(), NULL, tmp)) { failed = false; } } if (failed) { DEBUG_WARNING(true, ("Failed to transfer prototype %s to crafting " "tool %s", prototype.getNetworkId().getValueString().c_str(), myIdString)); return; } } } sync->setPrototype(CachedNetworkId(prototype)); } else { sendControllerMessageToAuthServer(CM_setCraftingPrototype, new MessageQueueGenericValueType(prototype.getNetworkId())); } } // TangibleObject::setCraftingPrototype //---------------------------------------------------------------------- /** * Removes the temporary prototype from the crafting tool and * deletes it. */ void TangibleObject::clearCraftingPrototype(void) { if(isAuthoritative()) { const std::string myId(getNetworkId().getValueString()); const char * myIdString = myId.c_str(); UNREF(myIdString); // needed for release mode // make sure we are a crafting tool if (!isCraftingTool() || getSynchronizedUi() == NULL) { DEBUG_WARNING(true, ("Tried to get non-crafting tool " "object %s", myIdString)); return; } CraftingToolSyncUi * sync = dynamic_cast( getSynchronizedUi()); if (sync == NULL) return; const CachedNetworkId & prototypeId = sync->getPrototype(); ServerObject * object = safe_cast(prototypeId.getObject()); if (object != NULL) { object->permanentlyDestroy(DeleteReasons::Consumed); } else { // double-check the prototype slot to make sure nothing is in it SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this); NOT_NULL(container); Container::ContainerErrorCode error; const Container::ContainedItem & contents = container->getObjectInSlot( getCraftingPrototypeSlotId(), error); if (contents.getObject() != NULL) { safe_cast(contents.getObject())->permanentlyDestroy( DeleteReasons::Consumed); } } sync->setPrototype(CachedNetworkId::cms_cachedInvalid); } else { sendControllerMessageToAuthServer(CM_clearCraftingPrototype, 0); } } // TangibleObject::clearCraftingPrototype // ---------------------------------------------------------------------- /** * Sets the visible component string for this object. Note m_components is * write-only on the server because this variable exists only to provide * visual data to the client. * * @param components component table ids of visible components attached to this object */ void TangibleObject::setVisibleComponents(const std::vector & components) { if(isAuthoritative()) { m_components.clear (); for (uint i = 0; i < components.size (); ++i) m_components.insert(components [i]); } else { sendControllerMessageToAuthServer(CM_setVisibleComponents, new MessageQueueGenericValueType >(components)); } } // ---------------------------------------------------------------------- void TangibleObject::forceExecuteCommand(Command const &command, NetworkId const &targetId, Unicode::String const ¶ms, Command::ErrorCode &status, bool commandIsFromCommandQueue) { if (!isAuthoritative()) { Controller *controller = getController(); if (controller) { safe_cast(controller)->appendMessage( CM_commandQueueForceExecuteCommandFwd, 0.0f, new MessageQueueCommandQueueForceExecuteCommandFwd(command.m_commandHash, targetId, params, status, commandIsFromCommandQueue), GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_AUTH_SERVER); } return; } if (status == Command::CEC_Success && ConfigServerGame::getLogAllCommands()) { LOGU( "Command", ("%s>%s %s", getNetworkId().getValueString().c_str(), targetId.getValueString().c_str(), command.m_commandName.c_str()), params); } if (getClient() && getClient()->isGod()) { CreatureObject* creatureObject = asCreatureObject(); if (creatureObject) { LOGU( "CustomerService", ("Avatar: %s executed command: (%s) %s", PlayerObject::getAccountDescription(creatureObject).c_str(), targetId.getValueString().c_str(), command.m_commandName.c_str()), params); } } if (getObjVars().hasItem("cheater")) { CreatureObject* creatureObject = asCreatureObject(); if (creatureObject) { LOGU( "CustomerService", ("SuspectedCheaterChannel: %s executed command: (%s) %s", PlayerObject::getAccountDescription(creatureObject).c_str(), targetId.getValueString().c_str(), command.m_commandName.c_str()), params); } } PROFILER_BLOCK_DEFINE(profileBlockCommandIsFromCommandQueue, "TangibleObject::forceExecuteCommand commandIsFromCommandQueue"); if (commandIsFromCommandQueue) PROFILER_BLOCK_ENTER(profileBlockCommandIsFromCommandQueue); // attach temp script bool needDetach = false; if (command.m_tempScript.length()) { int ret = getScriptObject()->attachScript(command.m_tempScript, true); if (ret != SCRIPT_OVERRIDE) needDetach = true; } // do script call bool commandOverride = false; if (status == Command::CEC_Success) { if (!command.m_scriptHook.empty()) { ScriptParams scriptParams; scriptParams.addParam(targetId); scriptParams.addParam(params); scriptParams.addParam(command.m_defaultTime); if (getScriptObject()->callScriptCommandHandler(command.m_scriptHook, scriptParams) == SCRIPT_OVERRIDE) commandOverride = true; } } else { if (!command.m_failScriptHook.empty()) { ScriptParams scriptParams; scriptParams.addParam(targetId); scriptParams.addParam(params); scriptParams.addParam(command.m_defaultTime); if (getScriptObject()->callScriptCommandHandler(command.m_failScriptHook, scriptParams) == SCRIPT_OVERRIDE) { commandOverride = true; // an override in a failure script handler should mean to mark it as a success (used for standing from feign death) status = Command::CEC_Success; } } } // detach temp scripts if (needDetach) getScriptObject()->detachScript(command.m_tempScript); if ( commandOverride && commandIsFromCommandQueue ) { CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { queue->cancelCurrentCommand(); } } // do c function call if not overridden by script if (!commandOverride) { if (status == Command::CEC_Success) { if (command.m_cppHook) (*command.m_cppHook)(command, getNetworkId(), targetId, params); } else { if (command.m_failCppHook) (*command.m_failCppHook)(command, getNetworkId(), targetId, params); } } if (commandIsFromCommandQueue) PROFILER_BLOCK_LEAVE(profileBlockCommandIsFromCommandQueue); } // ---------------------------------------------------------------------- void TangibleObject::setCondition(int condition) { if (isAuthoritative()) m_condition = (m_condition.get() | condition); else sendControllerMessageToAuthServer(CM_setCondition, new MessageQueueGenericValueType(condition)); } // ---------------------------------------------------------------------- void TangibleObject::clearCondition(int condition) { if (isAuthoritative()) m_condition = (m_condition.get() & (~condition)); else sendControllerMessageToAuthServer(CM_clearCondition, new MessageQueueGenericValueType(condition)); } // ---------------------------------------------------------------------- bool TangibleObject::isDisabled() const { return hasCondition(ServerTangibleObjectTemplate::C_disabled); } // ---------------------------------------------------------------------- void TangibleObject::setDisabled(bool disabled) { if (disabled) setCondition(ServerTangibleObjectTemplate::C_disabled); else clearCondition(ServerTangibleObjectTemplate::C_disabled); } // ---------------------------------------------------------------------- const NetworkId & TangibleObject::getOwnerId() const { return m_ownerId.get(); } // ---------------------------------------------------------------------- bool TangibleObject::isOwner(NetworkId const &id, Client const *client) const { if (id == getOwnerId()) return true; if (client && client->isGod()) return true; return false; } // ---------------------------------------------------------------------- /** * Determine the initial visibility state of an object when it is created. */ void TangibleObject::initializeVisibility() { if (isAuthoritative()) { const ServerTangibleObjectTemplate * myTemplate = safe_cast< const ServerTangibleObjectTemplate *>(getObjectTemplate()); if (myTemplate != NULL) { bool visible = false; for (size_t i = 0; i < myTemplate->getVisibleFlagsCount(); ++i) { if (myTemplate->getVisibleFlags(i) == ServerObjectTemplate::VF_player) { visible = true; break; } } setVisible(visible); } } else { WARNING(true, ("TangibleObject::initializeVisibility called on non-authoritative object %s", getNetworkId().getValueString().c_str())); } } // ---------------------------------------------------------------------- void TangibleObject::setVisible(bool isVisible) { if (isAuthoritative()) m_visible = isVisible; else sendControllerMessageToAuthServer(CM_setVisible, new MessageQueueGenericValueType(isVisible)); } // ---------------------------------------------------------------------- void TangibleObject::setHidden(bool const hide) { if (isAuthoritative()) m_hideFromClient = hide; else sendControllerMessageToAuthServer(CM_hideFromClient, new MessageQueueGenericValueType(hide)); } // ---------------------------------------------------------------------- void TangibleObject::visibilityDataModified() { if (isInWorld()) { if (isVisible() && !isHidden()) { // show the object const TriggerVolume * triggerVolume = getTriggerVolume(NetworkTriggerVolumeNamespace::NetworkTriggerVolumeName); if (triggerVolume != NULL) { std::vector observers; ServerWorld::findPlayerCreaturesInRange(getPosition_w(), triggerVolume->getRadius(), observers); if (!observers.empty()) ObserveTracker::onObjectMadeVisibleTo(*this, observers); } } else { // hide the object ObserveTracker::onObjectMadeInvisible(*this); // if the object is hidden, show the object to // players who have passively revealed the object if (isVisible() && isHidden() && !m_passiveRevealPlayerCharacter.empty()) { const TriggerVolume * triggerVolume = getTriggerVolume(NetworkTriggerVolumeNamespace::NetworkTriggerVolumeName); if (triggerVolume != NULL) { std::vector possibleObservers; ServerWorld::findPlayerCreaturesInRange(getPosition_w(), triggerVolume->getRadius(), possibleObservers); if (!possibleObservers.empty()) { std::vector allowedObservers; for (std::vector::const_iterator i = possibleObservers.begin(); i != possibleObservers.end(); ++i) { if (m_passiveRevealPlayerCharacter.contains((*i)->getNetworkId())) allowedObservers.push_back(*i); } if (!allowedObservers.empty()) ObserveTracker::onObjectMadeVisibleTo(*this, allowedObservers); } } } } } } bool TangibleObject::isVisibleOnClient(Client const &client) const { // Everyone can see visible objects. // Gods and clients on the buildcluster can see everything. if (client.isGod() || ConfigServerGame::getBuildCluster() || (isVisible() && !isHidden())) return true; // Invisible objects are visible to a non-god client if the client's // creature contains (or indirectly contains) the invisible object. ServerObject const * const characterObject = client.getCharacterObject(); { for (ServerObject const *o = this; o; o = safe_cast(ContainerInterface::getContainedByObject(*o))) { if (o == characterObject) return true; if (o->isInWorld() || o->asCellObject()) break; } } // Invisible objects are visible to a non-god client if they are in // the client's character's containment chain. { for (ServerObject const *o = characterObject; o; o = safe_cast(ContainerInterface::getContainedByObject(*o))) if (o == this) return true; } if (isVisible()) { // if the client's character is the owner, then he can see it. const CachedNetworkId ownerId(getOwnerId()); if (ownerId == characterObject->getNetworkId()) return true; // if the client's character is grouped with the the owner, then he can see it. const CreatureObject * owner = safe_cast(ownerId.getObject()); if (owner != NULL) { const GroupObject * group = owner->getGroup(); if (group != NULL) { if (group->isGroupMember(characterObject->getNetworkId())) return true; } } // if the client's character has passively revealed the hidden object, then he can see it. if (m_passiveRevealPlayerCharacter.contains(characterObject->getNetworkId())) return true; } return false; } // ---------------------------------------------------------------------- /** * Tests if this item will encumber a player when equipped. * * @return true if the item has encumbrance data */ bool TangibleObject::hasEncumbrances() const { return getObjVars().hasItem(OBJVAR_ARMOR_BASE + '.' + OBJVAR_ARMOR_ENCUMBRANCE); } // TangibleObject::hasEncumbrances // ---------------------------------------------------------------------- /** * Returns the encumbrance values this object applies to a player when equipped. * * @param encumbrances vector to be filled in with encumbrance values for the health, * action, and mind groups; will be empty if this object does not * encumber a creature * * @return true if encumbrances has good data, false if not */ bool TangibleObject::getEncumbrances(std::vector & encumbrances) const { static int datatable_type_col = -1; static int datatable_min_encumbrance_col = -1; static int datatable_max_encumbrance_col = -1; // NOTE: it might be better if we do all this via script, to reduce // duplicate functionality encumbrances.clear(); DataTable * dt = DataTableManager::getTable(DATATABLE_ARMOR, true); if (dt == NULL) return false; else if (datatable_type_col == -1) { // cache the column indexes datatable_type_col = dt->findColumnNumber(DATATABLE_TYPE_COL); datatable_min_encumbrance_col = dt->findColumnNumber(DATATABLE_MIN_ENCUMBRANCE_COL); datatable_max_encumbrance_col = dt->findColumnNumber(DATATABLE_MAX_ENCUMBRANCE_COL); if (datatable_type_col < 0 || datatable_min_encumbrance_col < 0 || datatable_max_encumbrance_col < 0) { WARNING_STRICT_FATAL(true, ("TangibleObject::getEncumbrances: armor datatable missing expected columns")); return false; } } // get the encumbrance values from the objvars float baseEncumbrance = 0; if (!getObjVars().getItem(OBJVAR_ARMOR_BASE + '.' + OBJVAR_ARMOR_ENCUMBRANCE, baseEncumbrance)) return false; std::vector encumbranceSplit; if (!getObjVars().getItem(OBJVAR_ARMOR_BASE + '.' + OBJVAR_ENCUMBRANCE_SPLIT, encumbranceSplit)) return false; if (encumbranceSplit.size() != 3) return false; // get the actual encumbrance range from the armor datatable int armorLevel; if (!getObjVars().getItem(OBJVAR_ARMOR_BASE + "." + OBJVAR_ARMOR_LEVEL, armorLevel)) return false; char armorLevelRowName[64]; snprintf(armorLevelRowName, sizeof(armorLevelRowName), "%s%d", DATATABLE_FINAL_ROW.c_str(), armorLevel); int armorLevelRow = dt->searchColumnInt(datatable_type_col, Crc::calculate(armorLevelRowName)); if (armorLevelRow < 0) return false; int minEncumbrance = dt->getIntValue(datatable_min_encumbrance_col, armorLevelRow); int maxEncumbrance = dt->getIntValue(datatable_max_encumbrance_col, armorLevelRow); // scale the base encumbrance by the actual encumbrance range baseEncumbrance = baseEncumbrance * (maxEncumbrance - minEncumbrance) + minEncumbrance; // split the base encumbrace into the three attrib groups encumbrances.resize(encumbranceSplit.size()); std::vector::iterator j = encumbrances.begin(); for (std::vector::const_iterator i = encumbranceSplit.begin(); i != encumbranceSplit.end(); ++i, ++j) { *j = static_cast((*i) * baseEncumbrance); } return true; } // ---------------------------------------------------------------------- /** * Returns the slot id of the slot we use to hold temporary crafting prototypes. * * @return the prototype slot id */ const SlotId & TangibleObject::getCraftingPrototypeSlotId() { static const SlotId prototypeSlotId(SlotIdManager::findSlotId(TEMP_PROTOTYPE_SLOT_NAME)); return prototypeSlotId; } // ---------------------------------------------------------------------- /** * Returns the slot id of the slot we use to hold temporary manf schematics. * * @return the schematic slot id */ const SlotId & TangibleObject::getCraftingManufactureSchematicSlotId() { static const SlotId schematicSlotId(SlotIdManager::findSlotId(TEMP_SCHEMATIC_SLOT_NAME)); return schematicSlotId; } // ---------------------------------------------------------------------- /** * Generates an id to allow players to identify a crafted component. NOTE: this * returns a pointer to a static buffer, so make sure you save off the number * before calling this function again. * * @param id network id that the serial number will be generated from * * @return the serial number */ const char * TangibleObject::generateSerialNumber(const NetworkId & id) { static char buffer[11] = {'(', 0, 0, 0, 0, 0, 0, 0, 0, ')', 0}; int i; char c; uint64 mask; #if defined(WIN32) uint64 value = (id.getValue() & 0x000000FFFFFFFFFFi64); #elif defined(linux) uint64 value = (id.getValue() & 0x000000FFFFFFFFFFULL); #else #error unsupported OS #endif for (i = 0; i < 10; ++i) { mask = ((value >> 4) | ((value & 0x0F) << 36)); value ^= mask; } for (i = 1; i < 9; ++i) { c = static_cast(value & 0x1F); value >>= 5; if (c < 10) c += '0'; else c += ('a' - 10); buffer[i] = c; } return buffer; } // TangibleObject::generateSerialNumber //---------------------------------------------------------------------- /** * Returns the attribute bonuses this object grants when equipped. * * @param attribBonuses list to be filled in with the bonuses */ void TangibleObject::getAttribBonuses(std::vector > & attribBonuses) const { attribBonuses.clear(); DynamicVariableList::NestedList mods(getObjVars(), OBJVAR_ATTRIBUTE_BONUS); for (DynamicVariableList::NestedList::const_iterator i = mods.begin(); i != mods.end(); ++i) { int bonus = 0; i.getValue(bonus); int attribute = atoi(i.getName().c_str()); if (isdigit(i.getName()[0]) && attribute >= 0 && attribute < Attributes::NumberOfAttributes) { if (bonus > ConfigServerGame::getMaxItemAttribBonus()) bonus = ConfigServerGame::getMaxItemAttribBonus(); else if (bonus < -ConfigServerGame::getMaxItemAttribBonus()) bonus = -ConfigServerGame::getMaxItemAttribBonus(); attribBonuses.push_back(std::make_pair(attribute, bonus)); } else { WARNING(true, ("Object %s has invalid attribute bonus objvar name of %s", getNetworkId().getValueString().c_str(), i.getName().c_str())); } } } // TangibleObject::getAttribBonuses /** * Returns the attribute bonuses this object grants when equipped. * * @param attribBonuses list of length 9 to be filled in with the bonuses * * @return true if we have bonuses, false if not */ bool TangibleObject::getAttribBonuses(std::vector & attribBonuses) const { attribBonuses.clear(); attribBonuses.resize(Attributes::NumberOfAttributes, 0); bool hasBonues = false; typedef std::vector > BonusPairs; BonusPairs pairedBonuses; getAttribBonuses(pairedBonuses); for (BonusPairs::const_iterator i = pairedBonuses.begin(); i != pairedBonuses.end(); ++i) { int index = (*i).first; if (index >=0 && index < Attributes::NumberOfAttributes) { attribBonuses[index] = (*i).second; hasBonues = true; } } return hasBonues; } // TangibleObject::getAttribBonuses //---------------------------------------------------------------------- /** * Returns the attribute bonus of this object for a given attribute. * * @param attribute the attribute * * @return the bonus for the attribute */ int TangibleObject::getAttribBonus(int attribute) const { if (attribute < 0 || attribute >= Attributes::NumberOfAttributes) return 0; DynamicVariableList::NestedList mods(getObjVars(), OBJVAR_ATTRIBUTE_BONUS); char buffer[8]; snprintf(buffer, sizeof(buffer), "%d", attribute); int bonus = 0; IGNORE_RETURN(mods.getItem(buffer, bonus)); if (bonus > ConfigServerGame::getMaxItemAttribBonus()) bonus = ConfigServerGame::getMaxItemAttribBonus(); else if (bonus < -ConfigServerGame::getMaxItemAttribBonus()) bonus = -ConfigServerGame::getMaxItemAttribBonus(); return bonus; } //---------------------------------------------------------------------- /** * Sets the attribute bonus this object applies when equipped. * * @param attribute the attribute * @param bonus the bonus */ void TangibleObject::setAttribBonus(int attribute, int bonus) { if (attribute < 0 || attribute >= Attributes::NumberOfAttributes) return; char buffer[8]; snprintf(buffer, sizeof(buffer), "%d", attribute); if (bonus != 0) setObjVarItem(OBJVAR_ATTRIBUTE_BONUS + '.' + buffer, bonus); else removeObjVarItem(OBJVAR_ATTRIBUTE_BONUS + '.' + buffer); } //---------------------------------------------------------------------- /** * Returns the skill mod bonuses this object grants when equipped. * * @param skillModBonuses list to be filled in with the bonuses */ void TangibleObject::getSkillModBonuses(std::vector > & skillModBonuses, bool includeCategorizedSkillmod /*= true*/) const { static std::map mapSkillModBonuses; mapSkillModBonuses.clear(); DynamicVariableList::NestedList mods(getObjVars(), OBJVAR_SKILLMOD_BONUS); for (DynamicVariableList::NestedList::const_iterator i = mods.begin(); i != mods.end(); ++i) { int bonus = 0; i.getValue(bonus); std::string const name = i.getName(); std::map::iterator iterFind = mapSkillModBonuses.find(name); if (iterFind != mapSkillModBonuses.end()) iterFind->second += bonus; else IGNORE_RETURN(mapSkillModBonuses.insert(std::make_pair(name, bonus))); } if (includeCategorizedSkillmod) getCategorizedSkillModBonuses(*this, OBJVAR_CATEGORIZED_SKILLMOD_BONUS, mapSkillModBonuses); skillModBonuses.clear(); for (std::map::const_iterator iterMapSkillModBonuses = mapSkillModBonuses.begin(); iterMapSkillModBonuses != mapSkillModBonuses.end(); ++iterMapSkillModBonuses) { if (iterMapSkillModBonuses->second != 0) skillModBonuses.push_back(std::make_pair(iterMapSkillModBonuses->first, iterMapSkillModBonuses->second)); } } // TangibleObject::getSkillModBonuses //---------------------------------------------------------------------- /** * Returns the skill mod bonus of this object for a given skill mod. * * @param skillMod the skill mod name * * @return the bonus for the skill mod */ int TangibleObject::getSkillModBonus(const std::string & skillMod, bool includeCategorizedSkillmod /*= true*/) const { DynamicVariableList::NestedList mods(getObjVars(), OBJVAR_SKILLMOD_BONUS); int value = 0; IGNORE_RETURN(mods.getItem(skillMod, value)); if (includeCategorizedSkillmod) value += getCategorizedSkillModBonus(*this, OBJVAR_CATEGORIZED_SKILLMOD_BONUS, skillMod); return value; } // TangibleObject::getSkillModBonus //---------------------------------------------------------------------- /** * Sets the base skill mod bonus this object applies when equipped. * * @param skillMod the skill mod name * @param bonus the bonus */ void TangibleObject::setSkillModBonus(const std::string & skillMod, int bonus) { std::string const skillModObjvar = OBJVAR_SKILLMOD_BONUS + '.' + skillMod; if (bonus < 0) { setObjVarItem(skillModObjvar, bonus); return; } else if (bonus == 0) { removeObjVarItem(skillModObjvar); return; } int const currentTotalBonus = getSkillModBonus(skillMod); int const currentBonus = getSkillModBonus(skillMod, false); if ((currentTotalBonus - currentBonus + bonus) > ConfigServerGame::getMaxObjectSkillModBonus()) bonus = std::max(0, ConfigServerGame::getMaxObjectSkillModBonus() - currentTotalBonus + currentBonus); if (bonus != 0) setObjVarItem(skillModObjvar, bonus); else removeObjVarItem(skillModObjvar); } // TangibleObject::setSkillModBonus //---------------------------------------------------------------------- void TangibleObject::setSkillModBonus(const std::string & category, const std::string & skillMod, int bonus) { if (category.empty()) return; std::string const skillModObjvar = OBJVAR_CATEGORIZED_SKILLMOD_BONUS + '.' + category + '.' + skillMod; if (bonus < 0) { setObjVarItem(skillModObjvar, bonus); return; } else if (bonus == 0) { removeObjVarItem(skillModObjvar); return; } int const currentTotalBonus = getSkillModBonus(skillMod); int currentBonus = 0; IGNORE_RETURN(getObjVars().getItem(skillModObjvar, currentBonus)); if ((currentTotalBonus - currentBonus + bonus) > ConfigServerGame::getMaxObjectSkillModBonus()) bonus = std::max(0, ConfigServerGame::getMaxObjectSkillModBonus() - currentTotalBonus + currentBonus); if (bonus != 0) setObjVarItem(skillModObjvar, bonus); else removeObjVarItem(skillModObjvar); } // TangibleObject::setSkillModBonus //---------------------------------------------------------------------- void TangibleObject::removeCategorizedSkillModBonuses(const std::string & category) { if (category.empty()) return; std::string const skillModObjvar = OBJVAR_CATEGORIZED_SKILLMOD_BONUS + '.' + category; if (getObjVars().hasItem(skillModObjvar) && (getObjVars().getType(skillModObjvar) == DynamicVariable::LIST)) removeObjVarItem(skillModObjvar); } //---------------------------------------------------------------------- /** * Adds a skill mod bonus to a skill mod socket, if the object has an open socket. * * @param skillMod the skill mod name * @param bonus the bonus * * @return true if the bonus was added, false if the object didn't have an open socket */ bool TangibleObject::addSkillModSocketBonus(const std::string & skillMod, int bonus) { if (bonus > ConfigServerGame::getMaxSocketSkillModBonus()) bonus = ConfigServerGame::getMaxSocketSkillModBonus(); int sockets = getSkillModSockets(); if (sockets > 0) { int currentBonus = getSkillModBonus(skillMod, false); // only apply the bonus if it's greater than the current bonus /*if (bonus <= currentBonus) // This breaks multi-socketed items, so now we combine all values. return false;*/ // Not sure if this code was ever hit in the first place. setSkillModBonus(skillMod, currentBonus + bonus); // trigger scripts ScriptParams params; std::vector skillModParam; skillModParam.push_back(skillMod.c_str()); std::vector bonusParam; bonusParam.push_back(bonus); params.addParam(skillModParam); params.addParam(bonusParam); IGNORE_RETURN(getScriptObject()->trigAllScripts(Scripting::TRIG_SOCKET_USED, params)); } return sockets > 0; } // TangibleObject::addSkillModSocketBonus //---------------------------------------------------------------------- /** * Adds multiple skill mod bonuses to a skill mod socket, if the object has an * open socket. Note that this call will only use up one socket, no matter how * many bonuses are added. * * @param skillModBonuses the bonuses * * @return true if the bonus was added, false if the object didn't have an open socket */ bool TangibleObject::addSkillModSocketBonuses(const std::vector > & skillModBonuses) { int sockets = getSkillModSockets(); if (sockets > 0) { int i; int count = skillModBonuses.size(); std::vector bonusOrder(1, 0); for (i = 1; i < count; ++i) { std::vector::iterator j; for (j = bonusOrder.begin(); j != bonusOrder.end(); ++j) { if (skillModBonuses[i].second > skillModBonuses[*j].second) { bonusOrder.insert(j, i); break; } } if (j == bonusOrder.end()) bonusOrder.push_back(i); } bool someSucceeded = false; for (i = 0; i < count; ++i) { int index = bonusOrder[i]; if(addSkillModSocketBonus(skillModBonuses[index].first, skillModBonuses[index].second)) someSucceeded = true; } if(someSucceeded) { setSkillModSockets(sockets - 1); return true; } } return sockets > 0; } // TangibleObject::addSkillModSocketBonuses //---------------------------------------------------------------------- /** * Returns the number of available skill mod sockets. * * @return the number of sockets */ int TangibleObject::getSkillModSockets(void) const { int sockets = 0; if (getGameObjectType() == SharedObjectTemplate::GOT_misc_container_wearable) return 0; bool const isCybernetic = getGameObjectType() == SharedObjectTemplate::GOT_cybernetic_arm || getGameObjectType() == SharedObjectTemplate::GOT_cybernetic_legs; IGNORE_RETURN(getObjVars().getItem(OBJVAR_SKILLMOD_SOCKETS, sockets)); return isCybernetic ? sockets : std::min(sockets, ConfigServerGame::getMaxSkillModSockets()); } // TangibleObject::getSkillModSockets //---------------------------------------------------------------------- /** * Sets the number of available skill mod sockets. * * @param sockets the number of sockets */ void TangibleObject::setSkillModSockets(int sockets) { bool const isCybernetic = getGameObjectType() == SharedObjectTemplate::GOT_cybernetic_arm || getGameObjectType() == SharedObjectTemplate::GOT_cybernetic_legs; if (sockets > ConfigServerGame::getMaxSkillModSockets() && !isCybernetic) sockets = ConfigServerGame::getMaxSkillModSockets(); if (sockets > 0) setObjVarItem(OBJVAR_SKILLMOD_SOCKETS, sockets); else removeObjVarItem(OBJVAR_SKILLMOD_SOCKETS); } // TangibleObject::setSkillModSockets //---------------------------------------------------------------------- /** * Returns the amount of time left before a crafted prototype object will be done. * * @return the time left, in secs */ int TangibleObject::getCraftingToolPrototypeTime() const { int timeLeft = -1; float prototypeTime = 0; getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_TIME,prototypeTime); if (prototypeTime > 0) { int startTime =0; if (getObjVars().getItem(OBJVAR_CRAFTING_PROTOTYPE_START_TIME,startTime)) { const int currentTime = static_cast(ServerClock::getInstance().getGameTimeSeconds()); timeLeft = static_cast(prototypeTime + 0.5f) - (currentTime - startTime); } } return timeLeft; } // TangibleObject::getCraftingToolPrototypeTime //---------------------------------------------------------------------- void TangibleObject::getAttributesForCraftingTool (AttributeVector & data) const { char valueBuffer[32]; static const Unicode::String CRAFT_TOOL_WORKING (Unicode::narrowToWide("@crafting:tool_status_working")); static const Unicode::String CRAFT_TOOL_FINISHED (Unicode::narrowToWide("@crafting:tool_status_finished")); static const Unicode::String CRAFT_TOOL_READY (Unicode::narrowToWide("@crafting:tool_status_ready")); static const SlotId outputSlotId(SlotIdManager::findSlotId(MANF_OUTPUT_SLOT_NAME)); float craft_effectiveness = 0.0f; getObjVars ().getItem(OBJVAR_CRAFTING_STATIONMOD, craft_effectiveness); snprintf (valueBuffer, sizeof (valueBuffer), "%.2f", craft_effectiveness); data.push_back (std::make_pair(SharedObjectAttributes::craft_tool_effectiveness, Unicode::narrowToWide(valueBuffer))); // add some internal attribs for the crafting station state // see if we are making a prototype int prototypeTimeLeft = getCraftingToolPrototypeTime(); if (prototypeTimeLeft > 0) { data.push_back(std::make_pair(SharedObjectAttributes::craft_tool_status, CRAFT_TOOL_WORKING)); _itoa(prototypeTimeLeft, valueBuffer, 10); data.push_back(std::make_pair(SharedObjectAttributes::craft_tool_time, Unicode::narrowToWide(valueBuffer))); } else { // see if there is an object in the output hopper bool hasPrototype = false; SlottedContainer const * const slotContainer = ContainerInterface::getSlottedContainer(*this); if (slotContainer != NULL) { Container::ContainerErrorCode tmp = Container::CEC_Success; if (slotContainer->getObjectInSlot(outputSlotId, tmp) != Container::ContainedItem::cms_invalid) { hasPrototype = true; } } if (hasPrototype) data.push_back(std::make_pair(SharedObjectAttributes::craft_tool_status, CRAFT_TOOL_FINISHED)); else data.push_back(std::make_pair(SharedObjectAttributes::craft_tool_status, CRAFT_TOOL_READY)); } } // ---------------------------------------------------------------------- void TangibleObject::getAttributes(const NetworkId & playerId, AttributeVector &data) const { ServerObject::getAttributes(playerId, data); } // ---------------------------------------------------------------------- void TangibleObject::getAttributesForShipComponent (AttributeVector & data) const { ShipComponentData * const componentData = ShipComponentDataManager::create(*this); if(componentData) { componentData->getAttributes(data); delete componentData; } } // ---------------------------------------------------------------------- void TangibleObject::getAttributes(AttributeVector & data) const { ServerObject::getAttributes(data); char valueBuffer[32]; CreatureObject const * const thisCreature = asCreatureObject(); if (!thisCreature) { const int maxHit = getMaxHitPoints (); // Only consider tangible invulnerability rules for this case if (maxHit > 0 && !TangibleObject::isInvulnerable()) { snprintf (valueBuffer, sizeof (valueBuffer), "%d/%d", maxHit - getDamageTaken (), maxHit); data.push_back (std::make_pair (SharedObjectAttributes::condition, Unicode::narrowToWide (valueBuffer))); } const ServerObjectTemplate * const targetTemplate = safe_cast(getObjectTemplate()); if (targetTemplate) { bool playerCanMove = false; for (size_t i = 0; i < targetTemplate->getMoveFlagsCount(); ++i) { if (targetTemplate->getMoveFlags(i) == ServerObjectTemplate::MF_player) { playerCanMove = true; break; } } if (getCacheVersion() <= 0 && playerCanMove) { snprintf (valueBuffer, sizeof (valueBuffer), "%d", std::max (1, getVolume ())); data.push_back (std::make_pair (SharedObjectAttributes::volume, Unicode::narrowToWide (valueBuffer))); } } } const int got = getGameObjectType (); switch (got) { case SharedObjectTemplate::GOT_misc_drink: case SharedObjectTemplate::GOT_misc_food: case SharedObjectTemplate::GOT_misc_pharmaceutical: case SharedObjectTemplate::GOT_weapon_ranged_thrown: case SharedObjectTemplate::GOT_weapon_heavy_misc: case SharedObjectTemplate::GOT_misc_fishing_bait: { const int count = m_count.get (); if (count) { snprintf (valueBuffer, sizeof (valueBuffer), "%d", m_count.get ()); data.push_back (std::make_pair (SharedObjectAttributes::counter_uses_remaining, Unicode::narrowToWide (valueBuffer))); } } break; case SharedObjectTemplate::GOT_resource_container_pseudo: { int resourceValue = 0; for (int i = 0; i < Crafting::RA_numResourceAttributes; ++i) { const std::string ovName(OBJVAR_COMPONENT_DATA + "." + Crafting::getResourceAttributeName(i)); if (getObjVars().getItem(ovName, resourceValue)) { char buffer[24]; snprintf(buffer, sizeof(buffer), "%i", resourceValue); data.push_back(std::make_pair(Crafting::getResourceAttributeName(i),Unicode::narrowToWide(buffer))); } } } break; default: break; } const VolumeContainer * const volumeContainer = ContainerInterface::getVolumeContainer (*this); if (volumeContainer) { if (got != SharedObjectTemplate::GOT_misc_factory_crate) { const int curVol = volumeContainer->getCurrentVolume (); const int totVol = volumeContainer->getTotalVolume (); if (curVol >= 0 && totVol >= 0) { snprintf (valueBuffer, sizeof (valueBuffer), "%d/%d", curVol, totVol); data.push_back (std::make_pair (SharedObjectAttributes::contents, Unicode::narrowToWide (valueBuffer))); } } } std::vector > skillModBonuses; getSkillModBonuses(skillModBonuses); if (!skillModBonuses.empty()) { char buffer[16]; int count = skillModBonuses.size(); for (int i = 0; i < count; ++i) { _itoa(skillModBonuses[i].second, buffer, 10); data.push_back(std::make_pair(SharedObjectAttributes::cat_skill_mod_bonus + "." + "@stat_n:" + skillModBonuses[i].first, Unicode::narrowToWide(buffer))); } } std::vector attribBonuses; if (getAttribBonuses(attribBonuses) && !attribBonuses.empty()) { char buffer[16]; int const attribCount = attribBonuses.size(); for (int attrib = 0; attrib < attribCount; ++attrib) { int const bonus = attribBonuses[attrib]; if (bonus != 0) { _itoa(bonus, buffer, 10); data.push_back(std::make_pair(SharedObjectAttributes::cat_attrib_mod_bonus + "." + "@att_bonus_n:" + s_attributeBonusNames[attrib], Unicode::narrowToWide(buffer))); } } } int sockets = getSkillModSockets(); if (sockets > 0) { char buffer[16]; _itoa(sockets, buffer, 10); data.push_back(std::make_pair(SharedObjectAttributes::sockets, Unicode::narrowToWide(buffer))); } if (isCraftingTool()) getAttributesForCraftingTool (data); if (getCreatorId() != NetworkId::cms_invalid) data.push_back(std::make_pair(SharedObjectAttributes::crafter, getCreatorName())); if (isCrafted()) data.push_back(std::make_pair(SharedObjectAttributes::serial_number, Unicode::narrowToWide(generateSerialNumber(getCraftedId())))); if(GameObjectTypes::isTypeOf(got, SharedObjectTemplate::GOT_ship_component)) { getAttributesForShipComponent(data); } { // If this is a fake ship component in the interior of a ship, get its attributes from the ship ServerObject const * const cell = safe_cast(ContainerInterface::getContainedByObject(*this)); if (cell && cell->asCellObject()) { ServerObject const * const shipAsObject = safe_cast(ContainerInterface::getContainedByObject(*cell)); if (shipAsObject) { ShipObject const * const ship = shipAsObject->asShipObject(); if (ship) { int slot; if (getObjVars().getItem("intSlot",slot)) { ShipComponentData const * const componentData = ship->createShipComponentData(slot); if (componentData) { componentData->getAttributes(data); delete componentData; } } } } } } if (getObjVars().hasItem(OBJVAR_BIO_LINK_ID)) { // display the bio-link info if (getBioLink() == PENDING_BIO_LINK_NETWORK_ID) { data.push_back(std::make_pair(SharedObjectAttributes::bio_link, Unicode::narrowToWide("@"+SharedStringIds::bio_link_pending.getCanonicalRepresentation()))); } else { const std::string & bioLinkName = NameManager::getInstance().getPlayerFullName(getBioLink()); if (!bioLinkName.empty()) { data.push_back(std::make_pair(SharedObjectAttributes::bio_link, Unicode::narrowToWide(bioLinkName))); } else { LOG("CustomerService", ("Biolink: object %s is flagged as bio-linked " "to player %s that we can't find a name for", getNetworkId().getValueString().c_str(), PlayerObject::getAccountDescription(getBioLink()).c_str())); data.push_back(std::make_pair(SharedObjectAttributes::bio_link, Unicode::narrowToWide("@"+SharedStringIds::bio_link_unknown.getCanonicalRepresentation()))); } } } // No Trade { if (markedNoTrade()) { data.push_back(std::make_pair(SharedObjectAttributes::no_trade, Unicode::emptyString)); if (markedNoTradeShared(true)) { data.push_back(std::make_pair(SharedObjectAttributes::no_trade_shared, Unicode::emptyString)); } } else { GameScriptObject * const gso = const_cast(getScriptObject()); if (gso && gso->hasScript(NOMOVE_SCRIPT)) { data.push_back(std::make_pair(SharedObjectAttributes::no_trade, Unicode::emptyString)); if (markedNoTradeShared(false)) { data.push_back(std::make_pair(SharedObjectAttributes::no_trade_shared, Unicode::emptyString)); } } } } } // ---------------------------------------------------------------------- void TangibleObject::getAttributesForAuction(AttributeVector &data) const { static const std::string attributeAppearanceData("AppearanceData"); DEBUG_FATAL(getAppearanceData().length() > 1000,("Appearance data was more than 1000 characters long, too long to save.")); data.push_back(std::make_pair(attributeAppearanceData, Unicode::narrowToWide(getAppearanceData()))); ServerObject::getAttributesForAuction(data); } // ---------------------------------------------------------------------- void TangibleObject::showFlyText(const StringId &outputText, float scale, int r, int g, int b) { //-- Get the controller. Controller *const controller = getController(); if (!controller) { DEBUG_WARNING(true, ("showFlyText(): object [%s] has no controller.", getNetworkId().getValueString().c_str())); return; } //-- Send the showFlyText message to all clients. uint32 flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_CLIENT; if (isPlayerControlled()) flags |= GameControllerMessageFlags::DEST_AUTH_CLIENT; controller->appendMessage(static_cast(CM_showFlyText), 0.0f, new MessageQueueShowFlyText(getNetworkId(), outputText, scale, r, g, b, 0), flags); } // ---------------------------------------------------------------------- void TangibleObject::showFlyText(const Unicode::String &outputTextOOB, float scale, int r, int g, int b) { //-- Get the controller. Controller *const controller = getController(); if (!controller) { DEBUG_WARNING(true, ("showFlyText(): object [%s] has no controller.", getNetworkId().getValueString().c_str())); return; } //-- Send the showFlyText message to all clients. uint32 flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_CLIENT; if (isPlayerControlled()) flags |= GameControllerMessageFlags::DEST_AUTH_CLIENT; controller->appendMessage(static_cast(CM_showFlyText), 0.0f, new MessageQueueShowFlyText(getNetworkId(), outputTextOOB, scale, r, g, b, 0), flags); } // ---------------------------------------------------------------------- void TangibleObject::showFlyText(const Unicode::String &outputTextOOB, float scale, int r, int g, int b, int flags) { //-- Get the controller. Controller *const controller = getController(); if (!controller) { DEBUG_WARNING(true, ("showFlyText(): object [%s] has no controller.", getNetworkId().getValueString().c_str())); return; } //-- Send the showFlyText message to all clients. uint32 netFlags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_CLIENT; if (isPlayerControlled()) flags |= GameControllerMessageFlags::DEST_AUTH_CLIENT; controller->appendMessage(static_cast(CM_showFlyText), 0.0f, new MessageQueueShowFlyText(getNetworkId(), outputTextOOB, scale, r, g, b, flags), netFlags); } // ---------------------------------------------------------------------- void TangibleObject::showCombatText(const TangibleObject & attackerObject, const StringId &outputText, float scale, int r, int g, int b) { //-- Get the controller. Controller *const controller = getController(); if (!controller) { DEBUG_WARNING(true, ("showFlyText(): object [%s] has no controller.", getNetworkId().getValueString().c_str())); return; } //-- Send the showFlyText message to all clients. uint32 flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_CLIENT; if (isPlayerControlled()) flags |= GameControllerMessageFlags::DEST_AUTH_CLIENT; controller->appendMessage(static_cast(CM_showCombatText), 0.0f, new MessageQueueShowCombatText(getNetworkId(), attackerObject.getNetworkId(), outputText, scale, r, g, b, 0), flags); } // ---------------------------------------------------------------------- void TangibleObject::showCombatText(const TangibleObject & attackerObject, const Unicode::String &outputTextOOB, float scale, int r, int g, int b) { //-- Get the controller. Controller *const controller = getController(); if (!controller) { DEBUG_WARNING(true, ("showFlyText(): object [%s] has no controller.", getNetworkId().getValueString().c_str())); return; } //-- Send the showFlyText message to all clients. uint32 flags = GameControllerMessageFlags::SEND | GameControllerMessageFlags::RELIABLE | GameControllerMessageFlags::DEST_PROXY_CLIENT; if (isPlayerControlled()) flags |= GameControllerMessageFlags::DEST_AUTH_CLIENT; controller->appendMessage(static_cast(CM_showCombatText), 0.0f, new MessageQueueShowCombatText(getNetworkId(), attackerObject.getNetworkId(), outputTextOOB, scale, r, g, b, 0), flags); } // ---------------------------------------------------------------------- void TangibleObject::handleCMessageTo(const MessageToPayload &message) { static const std::string methodname_updateCounter = "C++updateCounter"; if (message.getMethod() == methodname_updateCounter) { if (isCraftingTool()) { int timeLeft = getCraftingToolPrototypeTime(); if (timeLeft <= 0) { // done timeLeft = 0; } else { // update the count 5 sec from now MessageToQueue::getInstance().sendMessageToC (getNetworkId(), methodname_updateCounter, std::string (), 5, false); } m_count = timeLeft; } } else if (message.getMethod() == "C++UpdatePvp") { Pvp::forceStatusUpdate(*this); } else if (message.getMethod() == "C++PurgeStructure") { LOG("CustomerService",("Purge: Purging structure %s",getNetworkId().getValueString().c_str())); rollupStructure(NetworkId(message.getDataAsString()),false); } else if (message.getMethod() == "C++WarnAboutPurge") { LOG("CustomerService",("Purge: Sending warning emails to the admins of structure %s",getNetworkId().getValueString().c_str())); rollupStructure(NetworkId(message.getDataAsString()),true); } else if (message.getMethod() == "C++PurgeVendor") { LOG("CustomerService",("Purge: Purging vendor %s",getNetworkId().getValueString().c_str())); permanentlyDestroy(DeleteReasons::Decay); // Script's onDestroy() method will handle cleanup } else if (message.getMethod() == "C++CheckPvpRegion") { // check for pvp region change if (isAuthoritative() && !isInvulnerable() && !isNonPvpObject()) { Pvp::checkForRegionChange(*this); } if (isPlayerControlled()) { std::map const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData(); std::map::const_iterator iterFind = connectedCharacterLfgData.find(getNetworkId()); if (iterFind != connectedCharacterLfgData.end()) { std::string regionName; std::string playerCityName; std::string const & sceneId = ServerWorld::getSceneId(); Vector const &position = findPosition_w(); Region const * const region = RegionMaster::getSmallestVisibleRegionAtPoint(sceneId, position.x, position.z); if (region != NULL) regionName = Unicode::wideToNarrow(region->getName()); int const cityId = CityInterface::getCityAtLocation(sceneId, static_cast(position.x), static_cast(position.z), 0); if (cityId > 0) playerCityName = CityInterface::getCityInfo(cityId).getCityName(); if (iterFind->second.locationRegion != regionName) ServerUniverse::setConnectedCharacterRegionData(getNetworkId(), regionName); if (iterFind->second.locationPlayerCity != playerCityName) ServerUniverse::setConnectedCharacterPlayerCityData(getNetworkId(), playerCityName); PlayerObject * const player = PlayerCreatureController::getPlayerObject(asCreatureObject()); if (player) player->updateGcwRegionInfo(); } } } else if (message.getMethod() == "C++ProcessHateOverTime") { if (!m_hateOverTime.empty()) { std::list expired; unsigned long const currentGameTime = ServerClock::getInstance().getGameTimeSeconds(); for (std::map > >::const_iterator iter = m_hateOverTime.begin(); iter != m_hateOverTime.end(); ++iter) { if (iter->second.second.first < currentGameTime) { IGNORE_RETURN(addHate(iter->first, iter->second.first * static_cast(std::min(currentGameTime, iter->second.second.second) - iter->second.second.first))); if (iter->second.second.second <= currentGameTime) { expired.push_back(iter->first); } else { m_hateOverTime.set(iter->first, std::make_pair(iter->second.first, std::make_pair(currentGameTime, iter->second.second.second))); } } } if (!expired.empty()) { for (std::list::const_iterator iterExpired = expired.begin(); iterExpired != expired.end(); ++iterExpired) { IGNORE_RETURN(m_hateOverTime.erase(*iterExpired)); } } if (!m_hateOverTime.empty()) { MessageToQueue::sendMessageToC(getNetworkId(), "C++ProcessHateOverTime", std::string(), 1, false); } } } else if (message.getMethod() == "C++DestroyDupedTcg5AtAtHeadItv") { LOG("CustomerService",("VeteranRewards: destroying duped tcg5_atat_head_itv item (%s) owned by (%s)", getNetworkId().getValueString().c_str(), getOwnerId().getValueString().c_str())); permanentlyDestroy(DeleteReasons::Replaced); } else ServerObject::handleCMessageTo(message); } //---------------------------------------------------------------------- CustomizationData *TangibleObject::fetchCustomizationData () { CustomizationDataProperty * const cdprop = safe_cast(getProperty (CustomizationDataProperty::getClassPropertyId())); return cdprop ? cdprop->fetchCustomizationData () : 0; } //---------------------------------------------------------------------- const CustomizationData *TangibleObject::fetchCustomizationData () const { const CustomizationDataProperty * const cdprop = safe_cast(getProperty (CustomizationDataProperty::getClassPropertyId())); return cdprop ? cdprop->fetchCustomizationData () : 0; } // ---------------------------------------------------------------------- void TangibleObject::calcPvpableState() { // a tangible is pvpable if it has hit points, has a shared template, does not have an arrangement descriptor, and is not player movable // or m_attackableOverride is true if (m_attackableOverride.get()) { setPvpable(true); return; } setPvpable(false); if (getMaxHitPoints() && getSharedTemplate() && !getSharedTemplate()->getArrangementDescriptor()) { ServerObjectTemplate const *objTemplate = safe_cast(getObjectTemplate()); for (size_t i = 0; i < objTemplate->getMoveFlagsCount(); ++i) if (objTemplate->getMoveFlags(i) == ServerObjectTemplate::MF_player) return; setPvpable(true); } } // ---------------------------------------------------------------------- bool TangibleObject::isInvulnerable() const { return hasCondition(ServerTangibleObjectTemplate::C_invulnerable); } // ---------------------------------------------------------------------- void TangibleObject::setInvulnerable(bool invulnerable) { if (invulnerable) { setCondition(ServerTangibleObjectTemplate::C_invulnerable); } else { clearCondition(ServerTangibleObjectTemplate::C_invulnerable); } GameScriptObject * const gameScriptObject = getScriptObject(); if (gameScriptObject != NULL) { ScriptParams scriptParams; scriptParams.addParam(hasCondition(ServerTangibleObjectTemplate::C_invulnerable)); IGNORE_RETURN(gameScriptObject->trigAllScripts(Scripting::TRIG_INVULNERABLE_CHANGED, scriptParams)); } } // ---------------------------------------------------------------------- bool TangibleObject::isInsured(void) const { return hasCondition(ServerTangibleObjectTemplate::C_insured); } // ---------------------------------------------------------------------- void TangibleObject::setInsured(bool insured) { if (insured) setCondition(ServerTangibleObjectTemplate::C_insured); else clearCondition(ServerTangibleObjectTemplate::C_insured); } // ---------------------------------------------------------------------- bool TangibleObject::isUninsurable(void) const { return hasCondition(ServerTangibleObjectTemplate::C_uninsurable); } // ---------------------------------------------------------------------- void TangibleObject::setUninsurable(bool uninsurable) { if (uninsurable) setCondition(ServerTangibleObjectTemplate::C_uninsurable); else clearCondition(ServerTangibleObjectTemplate::C_uninsurable); } // ---------------------------------------------------------------------- bool TangibleObject::wantSawAttackTriggers() const { if (isAuthoritative()) return hasCondition(ServerTangibleObjectTemplate::C_wantSawAttackTrigger); return false; } // ---------------------------------------------------------------------- void TangibleObject::setWantSawAttackTriggers(bool enable) { if (enable) setCondition(ServerTangibleObjectTemplate::C_wantSawAttackTrigger); else clearCondition(ServerTangibleObjectTemplate::C_wantSawAttackTrigger); } //---------------------------------------------------------------------- int TangibleObject::getCombatSkeleton() const { return static_cast((safe_cast(getObjectTemplate()))->getCombatSkeleton()); } // TangibleObject::getCombatSkeleton() //----------------------------------------------------------------------- bool TangibleObject::canDropInWorld() const { return true; } //------------------------------------------------------------------------------------------ int TangibleObject::getNumberOfLots() const { //-- fetch the shared object template const SharedTangibleObjectTemplate* const tangibleObjectTemplate = safe_cast (getSharedTemplate()); if (!tangibleObjectTemplate) { return 0; } //-- get the structure footprint const StructureFootprint* const structureFootprint = tangibleObjectTemplate->getStructureFootprint (); if (!structureFootprint) { return 0; } //-- return the number of lots return structureFootprint->getNumberOfStructureReservations (); } //------------------------------------------------------------------------------------------ bool TangibleObject::isOnAdminList(const CreatureObject& player) const { //shortcut for owners if (getOwnerId() == player.getNetworkId()) return true; if(player.getClient() && player.getClient()->isGod()) return true; // In script, the admin list is stored as a list of strings. // The format of the list is: // 1) All player are stored as network IDs represented as a string // 2) All guilds are stored as guild IDs represted as a string and prefixed with "guild:" typedef stdvector::fwd StringVector; static StringVector sv; sv.clear (); if (getObjVars().getItem("player_structure.admin.adminList", sv)) { const NetworkId requestorNetworkId = player.getNetworkId(); // Convert the network ID to a Unicode string Unicode::String requestorNetworkIdString = Unicode::narrowToWide( requestorNetworkId.getValueString() ); // Try to match the string for (StringVector::const_iterator it = sv.begin(); it != sv.end(); ++it) { const Unicode::String & adminString = *it; if (Unicode::caseInsensitiveCompare(adminString, requestorNetworkIdString)) { return true; } } } return false; } //------------------------------------------------------------------------------------------ bool TangibleObject::isOnHopperList(const CreatureObject& player) const { if (getOwnerId() == player.getNetworkId()) return true; if(player.getClient() && player.getClient()->isGod()) return true; // In script, the hopper list is stored as a list of strings. // The format of the list is: // 1) All player are stored as network IDs represented as a string // 2) All guilds are stored as guild IDs represted as a string and prefixed with "guild:" typedef stdvector::fwd StringVector; static StringVector sv; sv.clear (); if (getObjVars().getItem("player_structure.hopper.hopperList", sv)) { const NetworkId requestorNetworkId = player.getNetworkId(); // Convert the network ID to a Unicode string Unicode::String requestorNetworkIdString = Unicode::narrowToWide( requestorNetworkId.getValueString() ); // Try to match the string for (StringVector::const_iterator it = sv.begin(); it != sv.end(); ++it) { const Unicode::String & adminString = *it; if (Unicode::caseInsensitiveCompare(adminString, requestorNetworkIdString)) { return true; } } } return false; } // ---------------------------------------------------------------------- bool TangibleObject::isCustomizationDataModified() const { return getLocalFlag(LocalObjectFlags::TangibleObject_CustomizationDataModified); } // ---------------------------------------------------------------------- void TangibleObject::setCustomizationDataModified(bool modified) { setLocalFlag(LocalObjectFlags::TangibleObject_CustomizationDataModified, modified); scheduleForAlter(); } // ---------------------------------------------------------------------- void TangibleObject::setPvpable(bool pvpable) { bool oldPvpable = !isNonPvpObject(); setLocalFlag(LocalObjectFlags::TangibleObject_Pvpable, pvpable); if ((oldPvpable != pvpable) && !getObservers().empty()) { // did the object's "pvp sync" status change because of the Pvpable change? bool const wasPvpSync = PvpUpdateObserver::satisfyPvpSyncCondition(!oldPvpable, hasCondition(ServerTangibleObjectTemplate::C_invulnerable), (asCreatureObject() != NULL), getPvpFaction()); bool const isPvpSync = PvpUpdateObserver::satisfyPvpSyncCondition(!pvpable, hasCondition(ServerTangibleObjectTemplate::C_invulnerable), (asCreatureObject() != NULL), getPvpFaction()); if (wasPvpSync != isPvpSync) { // force pvp status update Pvp::forceStatusUpdate(*this); const std::set &observers = getObservers(); for (std::set::const_iterator i = observers.begin(); i != observers.end(); ++i) { if (isPvpSync) (*i)->addObservingPvpSync(this); else (*i)->removeObservingPvpSync(this); } } } if (isAuthoritative()) { if (pvpable && !oldPvpable) MessageToQueue::sendRecurringMessageToC(getNetworkId(), "C++CheckPvpRegion", std::string(), static_cast(getPvpRegionCheckTime())); else if (!pvpable && oldPvpable) MessageToQueue::cancelRecurringMessageTo(getNetworkId(), "C++CheckPvpRegion"); } } // ---------------------------------------------------------------------- int TangibleObject::getInterestRadius() const { if (isPlayerControlled()) return ConfigServerGame::getPlayerInterestRange(); ServerTangibleObjectTemplate const *myTemplate = safe_cast(getObjectTemplate()); return myTemplate->getInterestRadius(); } // ---------------------------------------------------------------------- void TangibleObject::getRequiredCertifications(std::vector & results) const { const SharedTangibleObjectTemplate *const sharedObjectTemplate = safe_cast(getSharedTemplate()); if (sharedObjectTemplate) { const int numRequired = sharedObjectTemplate->getCertificationsRequiredCount(); for(int i=0; igetCertificationsRequired(i)); } } } // ====================================================================== void TangibleObject::MaxHitPointsCallback::modified(TangibleObject &target, int oldValue, int value, bool) const { // if the values actually changed, and if it changed from 0 or to 0, then we need to recalculate pvp state if ((oldValue != value) && ((oldValue == 0) || (value == 0))) { bool oldNonPvpState = target.isNonPvpObject(); target.calcPvpableState(); // if pvp state changed, need to update clients if (oldNonPvpState != target.isNonPvpObject()) Pvp::forceStatusUpdate(target); } } // ---------------------------------------------------------------------- void TangibleObject::AttackableOverrideCallback::modified(TangibleObject &target, bool oldValue, bool value, bool) const { // if the values actually changed, then we need to recalculate pvp state if (oldValue != value) { bool oldNonPvpState = target.isNonPvpObject(); target.calcPvpableState(); // if pvp state changed, need to update clients if (oldNonPvpState != target.isNonPvpObject()) Pvp::forceStatusUpdate(target); } } // ---------------------------------------------------------------------- void TangibleObject::AppearanceDataCallback::modified(TangibleObject &target, const std::string &oldValue, const std::string &value, bool isLocal) const { UNREF(isLocal); //-- Log any time an authoritative, player-controlled object has customizations set to the empty string. // This most likely is something very wrong since the player is going to get reset to default cusotmizations. if (value.empty() && !oldValue.empty()) { if (target.isPlayerControlled() && target.isAuthoritative()) { std::string const oldAppearanceDataDump = CustomizationData::makeSelectDumpFormat(oldValue.c_str()); LOG("customization-data-loss", ("non-empty old customization data getting wiped, id=[%s],template=[%s],old m_appearanceData in SELECT DUMP format:[%s]", target.getNetworkId().getValueString().c_str(), target.getObjectTemplateName(), oldAppearanceDataDump.c_str() )); } } //-- Notify the object that its appearance data has been modified. target.appearanceDataModified(value); Object * const objectContainer = ContainerInterface::getContainedByObject(target); ServerObject * const serverContainer = objectContainer ? objectContainer->asServerObject() : NULL; TangibleObject * const tangibleContainer = serverContainer ? serverContainer->asTangibleObject() : NULL; if(tangibleContainer) tangibleContainer->onContainedItemAppearanceDataModified(target, oldValue, value); } // ---------------------------------------------------------------------- void TangibleObject::onContainedItemAppearanceDataModified(TangibleObject const & , std::string const & , std::string const & ) { //do nothing. Tangibles don't care if their contained objects have their appearance data change } // ---------------------------------------------------------------------- void TangibleObject::getByteStreamFromAutoVariable(const std::string & name, Archive::ByteStream & target) const { if(name == "customAppearance") { m_customAppearance.pack(target); } else if(name == "appearanceData") { m_appearanceData.pack(target); } else if(name == "incubatorResourceData") { std::string resourceData; static const uint32 crcIncubator = CrcLowerString::calculateCrc("object/tangible/crafting/station/incubator_station.iff"); if (getTemplateCrc() == crcIncubator) { DynamicVariableList const & objvars = getObjVars(); if (objvars.hasItem("power.name") && (DynamicVariable::STRING == objvars.getType("power.name"))) { std::string resourceName; IGNORE_RETURN(objvars.getItem("power.name", resourceName)); ResourceTypeObject const * const rto = ServerUniverse::getInstance().getResourceTypeByName(resourceName); if (rto && !rto->getParentClass().isRecycled() && !rto->getParentClass().isPermanent()) resourceData = rto->getResourceTypeDataForExport(); } } Archive::put(target, resourceData); } else { ServerObject::getByteStreamFromAutoVariable(name, target); } } // ---------------------------------------------------------------------- void TangibleObject::setAutoVariableFromByteStream(const std::string & name, const Archive::ByteStream & source) { Archive::ReadIterator ri(source); if(name == "customAppearance") { m_customAppearance.unpackDelta(ri); } else if(name == "appearanceData") { m_appearanceData.unpackDelta(ri); } else if(name == "incubatorResourceData") { std::string resourceData; Archive::get(ri, resourceData); static const uint32 crcIncubator = CrcLowerString::calculateCrc("object/tangible/crafting/station/incubator_station.iff"); if ((getTemplateCrc() == crcIncubator) && !resourceData.empty()) { DynamicVariableList const & objvars = getObjVars(); if (objvars.hasItem("power.name") && (DynamicVariable::STRING == objvars.getType("power.name"))) { NetworkId const importedResourceType = ResourceTypeObject::addImportedResourceType(resourceData); if (importedResourceType.isValid()) { if (importedResourceType.getValue() > NetworkId::cms_maxNetworkIdWithoutClusterId) { // need to tell all proxy game servers, if any, to create the imported // resource type, if it hasn't already been created on that game server ProxyList const &proxyList = getExposedProxyList(); if (!proxyList.empty()) { ServerMessageForwarding::begin(std::vector(proxyList.begin(), proxyList.end())); GenericValueTypeMessage > const addImportedResourceTypeMsg("AddImportedResourceType", std::make_pair(importedResourceType, resourceData)); ServerMessageForwarding::send(addImportedResourceTypeMsg); ServerMessageForwarding::end(); } } ResourceTypeObject const * const rto = ServerUniverse::getInstance().getResourceTypeById(importedResourceType); if (rto) { IGNORE_RETURN(setObjVarItem("power.name", rto->getResourceName())); if (importedResourceType.getValue() > NetworkId::cms_maxNetworkIdWithoutClusterId) IGNORE_RETURN(setObjVarItem("power.resourceData", resourceData)); else removeObjVarItem("power.resourceData"); } } } } } else { ServerObject::setAutoVariableFromByteStream(name, source); } addObjectToConcludeList(); } // ---------------------------------------------------------------------- /** * Sets a bio-link on this object. This object will only be able to be equipped by * the player who's id matches the bio-link id. * * @param playerId the player to link this object to */ void TangibleObject::setBioLink(const NetworkId & playerId) { if (playerId != NetworkId::cms_invalid) { if (isAuthoritative()) { setObjVarItem(OBJVAR_BIO_LINK_ID, playerId); } else { sendControllerMessageToAuthServer(CM_setBioLink, new MessageQueueGenericValueType(playerId)); } } } // ---------------------------------------------------------------------- /** * Sets the pending bio-link on this object. This will prevent the object from * being equipped by any player until a real bio-link is set. */ void TangibleObject::setPendingBioLink() { setBioLink(PENDING_BIO_LINK_NETWORK_ID); } // ---------------------------------------------------------------------- /** * Removes the bio-link on this object. */ void TangibleObject::clearBioLink() { if (isAuthoritative()) { removeObjVarItem(OBJVAR_BIO_LINK); } else { sendControllerMessageToAuthServer(CM_setBioLink, new MessageQueueGenericValueType(NetworkId::cms_invalid)); } } // ---------------------------------------------------------------------- /** * Returns the bio-link id of this object. * * @return the bio-link player id (NetworkId::cms_invalid if the object isn't bio-linked) */ NetworkId TangibleObject::getBioLink() const { NetworkId linkId; if (getObjVars().getItem(OBJVAR_BIO_LINK_ID, linkId)) return linkId; return NetworkId::cms_invalid; } // ---------------------------------------------------------------------- /** * Returns whether an object, or any contained object, is biolinked * * @return true if a biolinked object is found */ bool TangibleObject::isBioLinked() const { NetworkId const linkId = getBioLink(); if( (linkId != NetworkId::cms_invalid) && (linkId != PENDING_BIO_LINK_NETWORK_ID) ) { // If the network ID is valid and does not correspond to a pending bio-link // then we will assume that the tangible object is bio-linked return true; } return false; } // ---------------------------------------------------------------------- void TangibleObject::rollupStructure(NetworkId const & owner, bool warnOnly) { GameScriptObject* gso = getScriptObject(); if (gso) { gso->attachScript("structure.structure_rollup", true); ScriptParams params; params.addParam(owner); params.addParam(warnOnly); gso->trigAllScripts(Scripting::TRIG_DO_STRUCTURE_ROLLUP, params); gso->detachScript("structure.structure_rollup"); } } // ---------------------------------------------------------------------- CommandQueue * TangibleObject::getCommandQueue() const { return CommandQueue::getCommandQueue(*const_cast(this)); } //----------------------------------------------------------------------- void TangibleObject::commandQueueEnqueue(Command const &command, NetworkId const &targetId, Unicode::String const ¶ms, uint32 const sequenceId, bool const clearable, Command::Priority const priority, bool fromServer) { if(!fromServer) { if(command.m_fromServerOnly) { LOG("CustomerService", ("CheatChannel: attempt to queue server-only command %s from client on object %s", command.m_commandName.c_str(), getNetworkId().getValueString().c_str())); return; } } if (!isAuthoritative()) { sendControllerMessageToAuthServer(CM_commandQueueEnqueueFwd, new MessageQueueCommandQueueEnqueueFwd(sequenceId, command.m_commandHash, targetId, params, clearable, static_cast(priority), fromServer)); } else { CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { queue->enqueue(command, targetId, params, sequenceId, clearable, priority); } } } //----------------------------------------------------------------------- void TangibleObject::commandQueueRemove(uint32 const sequenceId) { if (!isAuthoritative()) { sendControllerMessageToAuthServer(CM_commandQueueRemove, new MessageQueueCommandQueueRemove(sequenceId, 0.0f, static_cast(Command::CEC_Success), 0)); } else { CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { queue->remove(sequenceId); } } } //----------------------------------------------------------------------- void TangibleObject::commandQueueClear() { // commandQueueRemove clears the queue when passed a 0 sequenceId commandQueueRemove(0); } //----------------------------------------------------------------------- bool TangibleObject::commandQueueHasCommandFromGroup(uint32 groupHash) const { CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { return queue->hasCommandFromGroup(groupHash); } return false; } //----------------------------------------------------------------------- void TangibleObject::commandQueueClearCommandsFromGroup(uint32 groupHash, bool force) { CommandQueue * const queue = getCommandQueue(); if (queue != NULL) { queue->clearCommandsFromGroup(groupHash, force); } } //----------------------------------------------------------------------- CombatEngineData::CombatData * TangibleObject::getCombatData() { return m_combatData; } //----------------------------------------------------------------------- CombatEngineData::CombatData const * TangibleObject::getCombatData() const { return m_combatData; } //----------------------------------------------------------------------- void TangibleObject::CombatStateChangedCallback::modified(TangibleObject & target, bool /*oldValue*/, bool newValue, bool /*local*/) const { if (newValue) { // Starting Combat LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", ("TangibleObject::Callbacks::CombatStateChanged::modified() STARTING COMBAT owner(%s:%s)", target.getNetworkId().getValueString().c_str(), Unicode::wideToNarrow(target.getEncodedObjectName()).c_str())); if (target.isAuthoritative()) { target.createCombatData(); target.m_combatStartTime = Os::getRealSystemTime(); if (!target.isPlayerControlled()) { ObjectTracker::addCombatAI(); } AICreatureController * const aiCreatureController = AICreatureController::asAiCreatureController(target.getController()); if (aiCreatureController != NULL) { aiCreatureController->markCombatStartLocation(); } if (target.getScriptObject() != NULL) { ScriptParams params; IGNORE_RETURN(target.getScriptObject()->trigAllScripts(Scripting::TRIG_ENTERED_COMBAT, params)); } target.scheduleForAlter(); } } else { // Exiting Combat LOGC(ConfigServerGame::isAiLoggingEnabled(), "debug_ai", ("TangibleObject::Callbacks::CombatStateChanged::modified() EXITING COMBAT owner(%s:%s)", target.getNetworkId().getValueString().c_str(), Unicode::wideToNarrow(target.getEncodedObjectName()).c_str())); if (target.isAuthoritative()) { if (!target.isPlayerControlled()) { ObjectTracker::removeCombatAI(); } //target.clearHateList(); target.commandQueueClearCommandsFromGroup(COMMAND_GROUP_COMBAT.getCrc()); if (target.getScriptObject() != NULL) { ScriptParams params; IGNORE_RETURN(target.getScriptObject()->trigAllScripts(Scripting::TRIG_EXITED_COMBAT, params)); } target.scheduleForAlter(); } } } //----------------------------------------------------------------------- int TangibleObject::getCombatDuration() const { int result = 0; if (isInCombat()) { result = static_cast(Os::getRealSystemTime() - m_combatStartTime.get()); } return result; } //----------------------------------------------------------------------- void TangibleObject::setAttackableOverride(bool attackable) { if (isAuthoritative()) { if (m_attackableOverride.get() != attackable) m_attackableOverride = attackable; } else { sendControllerMessageToAuthServer(CM_setAttackableOverride, new MessageQueueGenericValueType(attackable)); } } //----------------------------------------------------------------------- bool TangibleObject::hasAutoDeclineDuel() const { return getObjVars().hasItem(OBJVAR_DECLINE_DUEL); } //---------------------------------------------------------------------- int TangibleObject::getPassiveRevealRange(NetworkId const & target) const { Archive::AutoDeltaMap::const_iterator i = m_passiveReveal.find(target); if (i == m_passiveReveal.end()) return -1; return i->second; } //---------------------------------------------------------------------- void TangibleObject::addPassiveReveal(TangibleObject const & target, int range) { addPassiveReveal(target.getNetworkId(), range, (NULL != PlayerCreatureController::getPlayerObject(target.asCreatureObject()))); } //---------------------------------------------------------------------- void TangibleObject::addPassiveReveal(NetworkId const & target, int range, bool isTargetPlayerCharacter) { // and object cannot passively reveal itself if (target == getNetworkId()) return; // range must be > 0 if (range <= 0) return; if(isAuthoritative()) { m_passiveReveal.set(target, range); if (isTargetPlayerCharacter) m_passiveRevealPlayerCharacter.insert(target); } else { sendControllerMessageToAuthServer(CM_addPassiveReveal, new MessageQueueGenericValueType, NetworkId> >(std::make_pair(std::make_pair(range, (isTargetPlayerCharacter ? 1 : 0)), target))); } } //---------------------------------------------------------------------- void TangibleObject::removePassiveReveal(NetworkId const & target) { if(isAuthoritative()) { IGNORE_RETURN(m_passiveReveal.erase(target)); IGNORE_RETURN(m_passiveRevealPlayerCharacter.erase(target)); } else { sendControllerMessageToAuthServer(CM_removePassiveReveal, new MessageQueueGenericValueType(target)); } } //---------------------------------------------------------------------- void TangibleObject::removeAllPassiveReveal() { if(isAuthoritative()) { m_passiveReveal.clear(); m_passiveRevealPlayerCharacter.clear(); } else { sendControllerMessageToAuthServer(CM_removeAllPassiveReveal, 0); } } //---------------------------------------------------------------------- bool TangibleObject::setOverrideMapColor(unsigned char r, unsigned char g, unsigned char b) { if(!isAuthoritative()) { return false; } PackedArgb color(255, r, g, b); m_mapColorOverride.set(color.getArgb()); return true; } //---------------------------------------------------------------------- bool TangibleObject::clearOverrideMapColor() { if(!isAuthoritative()) { return false; } m_mapColorOverride.set(0); return true; } //---------------------------------------------------------------------- bool TangibleObject::getOverrideMapColor(unsigned char & r, unsigned char & g, unsigned char & b) const { PackedArgb color(m_mapColorOverride.get()); if(color.getA() == 0) { return false; } r = color.getR(); g = color.getG(); b = color.getB(); return true; } //---------------------------------------------------------------------- void TangibleObject::handleIncubatorCommit(CreatureObject const & owner, IncubatorCommitMessage const & msg) { // this operation *REQUIRES* that both the incubator and the // character manipulating the incubator be authoritative on // the same game server, so it's really not possible to // pass this handler over to the authoritative incubator, // since there's no guarantee that the character will be // authoritative on that game server either if (!isAuthoritative() || !owner.isAuthoritative()) return; GameScriptObject * const scriptObject = getScriptObject(); if (scriptObject) { ScriptParams params; params.addParam(owner.getNetworkId(), "playerId"); params.addParam(getNetworkId(), "terminalId"); params.addParam(msg.getSlot1Id(), "slot1Id"); params.addParam(msg.getSlot2Id(), "slot2Id"); params.addParam(msg.getSlot3Id(), "slot3Id"); params.addParam(msg.getSlot4Id(), "slot4Id"); params.addParam(msg.getInitialPointsSurvival(), "initialPointsSurvival"); params.addParam(msg.getInitialPointsBeastialResilience(),"initialPointsBeastialResilience"); params.addParam(msg.getInitialPointsCunning(),"initialPointsCunning"); params.addParam(msg.getInitialPointsIntelligence(),"initialPointsIntelligence"); params.addParam(msg.getInitialPointsAggression(),"initialPointsAggression"); params.addParam(msg.getInitialPointsHuntersInstinct(),"initialPointsHuntersInstinct"); params.addParam(msg.getTotalPointsSurvival(),"totalPointsSurvival"); params.addParam(msg.getTotalPointsBeastialResilience(),"totalPointsBeastialResilience"); params.addParam(msg.getTotalPointsCunning(),"totalPointsCunning"); params.addParam(msg.getTotalPointsIntelligence(),"totalPointsIntelligence"); params.addParam(msg.getTotalPointsAggression(),"totalPointsAggression"); params.addParam(msg.getTotalPointsHuntersInstinct(),"totalPointsHuntersInstinct"); params.addParam(msg.getTemperatureGauge(),"temperatureGauge"); params.addParam(msg.getNutrientGauge(),"nutrientGauge"); params.addParam(msg.getNewCreatureColorIndex(), "newCreatureColorIndex"); IGNORE_RETURN(scriptObject->trigAllScripts(Scripting::TRIG_INCUBATOR_COMMITTED, params)); } } void TangibleObject::handleIncubatorCancel(CreatureObject const & owner) { // this operation *REQUIRES* that both the incubator and the // character manipulating the incubator be authoritative on // the same game server, so it's really not possible to // pass this handler over to the authoritative incubator, // since there's no guarantee that the character will be // authoritative on that game server either if (!isAuthoritative() || !owner.isAuthoritative()) return; GameScriptObject * const scriptObject = getScriptObject(); if (scriptObject) { ScriptParams params; params.addParam(owner.getNetworkId(), "playerId"); params.addParam(getNetworkId(), "terminalId"); IGNORE_RETURN(scriptObject->trigAllScripts(Scripting::TRIG_INCUBATOR_CANCELED, params)); } } void TangibleObject::forceHateTarget(NetworkId const & target) { if(isAuthoritative()) { m_hateList.forceHateTarget(target); scheduleForAlter(); } else { sendControllerMessageToAuthServer(CM_forceHateTarget, new MessageQueueGenericValueType(target)); } } bool TangibleObject::isUserOnAccessList(NetworkId const& user) const { return (std::find(m_accessList.begin(), m_accessList.end(), user) != m_accessList.end()); } bool TangibleObject::isGuildOnAccessList(int guildId) const { return (std::find(m_guildAccessList.begin(), m_guildAccessList.end(), guildId) != m_guildAccessList.end()); } void TangibleObject::addUserToAccessList(const NetworkId &user) { if(isAuthoritative()) { if(m_accessList.find(user) == m_accessList.end()) { if(!hasCondition(C_locked)) // If we weren't locked before, we are now. setCondition(C_locked); m_accessList.insert(user); copyUserAccessListToObjVars(); } } else { sendControllerMessageToAuthServer(CM_addUserToAccessList, new MessageQueueGenericValueType< std::pair >(std::make_pair(-1, user) ) ); } } void TangibleObject::addGuildToAccessList(int guildId) { if(isAuthoritative()) { if(m_guildAccessList.find(guildId) == m_guildAccessList.end()) { if(!hasCondition(C_locked)) // If we weren't locked before, we are now. setCondition(C_locked); m_guildAccessList.insert(guildId); copyGuildAccessListToObjVars(); } } else { sendControllerMessageToAuthServer(CM_addUserToAccessList, new MessageQueueGenericValueType< std::pair >(std::make_pair(guildId, NetworkId::cms_invalid) ) ); } } void TangibleObject::removeUserFromAccessList(const NetworkId &user) { if(isAuthoritative()) { Archive::AutoDeltaSet::const_iterator iter = m_accessList.find(user); if(iter != m_accessList.end()) { m_accessList.erase(iter); if(m_accessList.empty()) { if(hasCondition(C_locked)) clearCondition(C_locked); // Containers with no access list are open to whomever. removeObjVarItem(s_accessListScriptVar); // Remove our objvars, no need for them anymore. return; } copyUserAccessListToObjVars(); } } else { sendControllerMessageToAuthServer(CM_removeUserFromAccessList, new MessageQueueGenericValueType< std::pair >(std::make_pair(-1, user))); } } void TangibleObject::removeGuildFromAccessList(int guildId) { if(isAuthoritative()) { Archive::AutoDeltaSet::const_iterator iter = m_guildAccessList.find(guildId); if(iter != m_guildAccessList.end()) { m_guildAccessList.erase(iter); if(m_guildAccessList.empty()) { if(hasCondition(C_locked) && m_accessList.empty()) clearCondition(C_locked); // Containers with no access list are open to whomever. removeObjVarItem(s_guildAccessListScriptVar); // Remove our objvars, no need for them anymore. return; } copyGuildAccessListToObjVars(); } } else { sendControllerMessageToAuthServer(CM_removeUserFromAccessList, new MessageQueueGenericValueType< std::pair >(std::make_pair(guildId, NetworkId::cms_invalid))); } } void TangibleObject::copyUserAccessListToObjVars() { std::vector newAccessList; Archive::AutoDeltaSet::const_iterator iter = m_accessList.begin(); for(; iter != m_accessList.end(); ++iter) newAccessList.push_back((*iter)); setObjVarItem(s_accessListScriptVar, newAccessList); } void TangibleObject::copyGuildAccessListToObjVars() { std::vector newAccessList; Archive::AutoDeltaSet::const_iterator iter = m_guildAccessList.begin(); for(; iter != m_guildAccessList.end(); ++iter) newAccessList.push_back((*iter)); setObjVarItem(s_guildAccessListScriptVar, newAccessList); } void TangibleObject::readInUserAccessListObjVars() { std::vector inputVector; if(getObjVars().getItem(s_accessListScriptVar, inputVector)) { // We have some obj vars to add to our list! std::vector::size_type i = 0; for( ; i < inputVector.size(); ++i) { m_accessList.insert(inputVector[i]); } } } void TangibleObject::readInGuildAccessListObjVars() { std::vector inputVector; if(getObjVars().getItem(s_guildAccessListScriptVar, inputVector)) { // We have some obj vars to add to our list! std::vector::size_type i = 0; for( ; i < inputVector.size(); ++i) { m_guildAccessList.insert(inputVector[i]); } } } void TangibleObject::clearUserAccessList() { if(isAuthoritative()) { if(hasCondition(C_locked) && m_guildAccessList.empty()) // No access list = total unlock. clearCondition(C_locked); m_accessList.clear(); removeObjVarItem(s_accessListScriptVar); // Remove our objvars, no need for them anymore. } else { sendControllerMessageToAuthServer(CM_clearUserAccessList, new MessageQueueGenericValueType(false)); } } void TangibleObject::clearGuildAccessList() { if(isAuthoritative()) { if(hasCondition(C_locked) && m_accessList.empty()) // No access list = total unlock. clearCondition(C_locked); m_guildAccessList.clear(); removeObjVarItem(s_guildAccessListScriptVar); // Remove our objvars, no need for them anymore. } else { sendControllerMessageToAuthServer(CM_clearUserAccessList, new MessageQueueGenericValueType(true)); } } void TangibleObject::getUserAccessList(std::vector & ids) { getObjVars().getItem(s_accessListScriptVar, ids); } void TangibleObject::getGuildAccessList(std::vector & ids) { getObjVars().getItem(s_guildAccessListScriptVar, ids); } void TangibleObject::addObjectEffect(std::string const & filename, std::string const & hardpoint, Vector const & offset, float scale, std::string const & label) { if(isAuthoritative()) { if(m_effectsMap.find(label) != m_effectsMap.end()) return; m_effectsMap.insert(label, std::make_pair(filename, std::make_pair( hardpoint, std::make_pair(offset, scale )))); } else sendControllerMessageToAuthServer(CM_addObjectEffect, new MessageQueueGenericValueType > > > >(std::make_pair(label, std::make_pair(filename, std::make_pair(hardpoint, std::make_pair(offset, scale)))))); } void TangibleObject::removeObjectEffect(std::string const & label) { if(isAuthoritative()) { Archive::AutoDeltaMap > > >::const_iterator iter = m_effectsMap.find(label); if(iter == m_effectsMap.end()) return; m_effectsMap.erase(iter); } else sendControllerMessageToAuthServer(CM_removeObjectEffect, new MessageQueueGenericValueType(label)); } bool TangibleObject::hasObjectEffect(std::string const & label) { return m_effectsMap.find(label) != m_effectsMap.end(); } void TangibleObject::removeAllObjectEffects() { if(isAuthoritative()) { m_effectsMap.clear(); } else sendControllerMessageToAuthServer(CM_removeAllObjectEffect, new MessageQueueGenericValueType(true)); } // ======================================================================