mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-14 00:01:30 -04:00
6401 lines
203 KiB
C++
Executable File
6401 lines
203 KiB
C++
Executable File
// ======================================================================
|
|
//
|
|
// 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 <cstdio>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
|
|
#pragma warning(disable:4355)
|
|
|
|
|
|
namespace TangibleObjectNamespace
|
|
{
|
|
int const s_minCombatDuration = 4;
|
|
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<std::string, int> & 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<std::string, int> & 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<std::string, int>::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<TangibleObject *>(getOwner())->clearCraftingManufactureSchematic();
|
|
}
|
|
if (m_prototype.get() != CachedNetworkId::cms_cachedInvalid)
|
|
{
|
|
safe_cast<TangibleObject *>(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<CachedNetworkId> m_prototype;
|
|
Archive::AutoDeltaVariable<CachedNetworkId> 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<NetworkId::NetworkIdType>(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 = nullptr;
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
TangibleObject::TangibleObject(const ServerTangibleObjectTemplate* newTemplate) :
|
|
ServerObject(newTemplate),
|
|
m_combatData(nullptr),
|
|
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<NetworkId::NetworkIdType>(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(nullptr),
|
|
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<int>(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<const SharedTangibleObjectTemplate*>(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<CustomizationDataProperty*>(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 != nullptr)
|
|
{
|
|
setAppearance(newAppearance);
|
|
} else {
|
|
DEBUG_WARNING(true, ("Missing appearance for TangibleObject::TangibleObject %s", appearanceString.c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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<SharedTangibleObjectTemplate const *> (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() != nullptr)
|
|
{
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync != nullptr)
|
|
{
|
|
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 != nullptr)
|
|
{
|
|
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];
|
|
|
|
// 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])
|
|
{
|
|
REPORT_LOG(true, ("\t%s(%d) : caller %d\n", file, line, i-callStackOffset));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete m_combatData;
|
|
m_combatData = nullptr;
|
|
|
|
if (!isPlayerControlled())
|
|
ObjectTracker::removeCombatAI();
|
|
}
|
|
|
|
if (m_npcConversation != nullptr)
|
|
{
|
|
delete m_npcConversation;
|
|
m_npcConversation = nullptr;
|
|
}
|
|
|
|
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 != nullptr)
|
|
{
|
|
setInvulnerable(newTemplate->getInvulnerable());
|
|
|
|
if (isAuthoritative())
|
|
{
|
|
initializeVisibility();
|
|
|
|
const ServerArmorTemplate * armorTemplate = newTemplate->getArmor();
|
|
if (armorTemplate != nullptr)
|
|
{
|
|
int const rating = static_cast<int>(armorTemplate->getRating());
|
|
if ( rating < static_cast<int>(ServerArmorTemplate::AR_armorNone)
|
|
|| rating > static_cast<int>(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<ServerTangibleObjectTemplate::CombatSkeleton>(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<int>(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 != nullptr)
|
|
{
|
|
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() != nullptr)
|
|
{
|
|
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<int>(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 != nullptr) ? object->asServerObject() : nullptr;
|
|
return (serverObject != nullptr) ? serverObject->asTangibleObject() : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
TangibleObject const * TangibleObject::asTangibleObject(Object const * object)
|
|
{
|
|
ServerObject const * serverObject = (object != nullptr) ? object->asServerObject() : nullptr;
|
|
return (serverObject != nullptr) ? serverObject->asTangibleObject() : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
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 == nullptr)
|
|
{
|
|
m_defaultSharedTemplate = safe_cast<const SharedObjectTemplate *>(
|
|
ObjectTemplateList::fetch(templateName));
|
|
WARNING_STRICT_FATAL(m_defaultSharedTemplate == nullptr, ("Cannot create "
|
|
"default shared object template %s", templateName.getString()));
|
|
if (m_defaultSharedTemplate != nullptr)
|
|
ExitChain::add (removeDefaultTemplate, "TangibleObject::removeDefaultTemplate");
|
|
}
|
|
return m_defaultSharedTemplate;
|
|
} // TangibleObject::getDefaultSharedTemplate
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Cleans up the default shared template.
|
|
*/
|
|
void TangibleObject::removeDefaultTemplate(void)
|
|
{
|
|
if (m_defaultSharedTemplate != nullptr)
|
|
{
|
|
m_defaultSharedTemplate->releaseReference();
|
|
m_defaultSharedTemplate = nullptr;
|
|
}
|
|
} // 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<LocationData>::const_iterator i;
|
|
for(i = m_locationTargets.begin(); i != m_locationTargets.end(); ++i)
|
|
{
|
|
if((*i).name == location.name)
|
|
{
|
|
m_locationTargets.set(static_cast<size_t>(std::distance(m_locationTargets.begin(), i)), location);
|
|
return;
|
|
}
|
|
}
|
|
m_locationTargets.push_back(location);
|
|
scheduleForAlter();
|
|
}
|
|
else
|
|
{
|
|
sendControllerMessageToAuthServer(CM_addLocationTarget, new MessageQueueGenericValueType<LocationData>(location));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void TangibleObject::removeLocationTarget(const Unicode::String & locationName)
|
|
{
|
|
if(isAuthoritative())
|
|
{
|
|
std::vector<LocationData>::const_iterator i;
|
|
for(i = m_locationTargets.begin(); i != m_locationTargets.end(); ++i)
|
|
{
|
|
if((*i).name == locationName)
|
|
{
|
|
m_locationTargets.erase(static_cast<size_t>(std::distance(m_locationTargets.begin(), i)));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendControllerMessageToAuthServer(CM_removeLocationTarget, new MessageQueueGenericValueType<Unicode::String>(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<CustomizationDataProperty*>(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<TangibleObject *> &items) const
|
|
{
|
|
SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this);
|
|
if (container == nullptr)
|
|
return;
|
|
|
|
std::vector<Container::ContainedItem> itemIds;
|
|
container->getObjectsForCombatBone(combatBone, itemIds);
|
|
|
|
std::vector<Container::ContainedItem>::const_iterator iter;
|
|
for (iter = itemIds.begin(); iter != itemIds.end(); ++iter)
|
|
{
|
|
Object * object = (*iter).getObject();
|
|
if (object != nullptr)
|
|
{
|
|
TangibleObject * item = dynamic_cast<TangibleObject *>(object);
|
|
if (item != nullptr)
|
|
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<TangibleObject *> items;
|
|
getEquippedItems(combatBone, items);
|
|
|
|
if (items.empty())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
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<unsigned long>(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<NetworkId> 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<LocationData>::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<CustomizationDataProperty*>(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<const CraftingToolSyncUi *>(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() != nullptr)
|
|
{
|
|
const CraftingToolSyncUi * sync = dynamic_cast<const CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync != nullptr)
|
|
{
|
|
Object * object = sync->getPrototype().getObject();
|
|
if (object != nullptr)
|
|
object->conclude();
|
|
object = sync->getManfSchematic().getObject();
|
|
if (object != nullptr)
|
|
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<Pvp::PvpType>(m_pvpType.get());
|
|
if (oldType != pvpType)
|
|
{
|
|
m_pvpType.set(static_cast<int>(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<Pvp::PvpType>(m_pvpFutureType.get());
|
|
if (oldFutureType != pvpFutureType)
|
|
{
|
|
m_pvpFutureType.set(static_cast<int>(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<Pvp::FactionId>(m_pvpFaction.get());
|
|
if (oldFaction != factionId)
|
|
{
|
|
m_pvpFaction.set(static_cast<uint32>(factionId));
|
|
if (getScriptObject())
|
|
{
|
|
ScriptParams params;
|
|
params.addParam(static_cast<int>(oldFaction));
|
|
params.addParam(static_cast<int>(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<int>::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<int>(::time(nullptr))));
|
|
removeObjVarItem("factionalHelper.timeStopHelpingRebel");
|
|
}
|
|
else if (PvpData::isRebelFactionId(m_pvpMercenaryFaction.get()))
|
|
{
|
|
IGNORE_RETURN(setObjVarItem("factionalHelper.timeStopHelpingRebel", static_cast<int>(::time(nullptr))));
|
|
removeObjVarItem("factionalHelper.timeStopHelpingImperial");
|
|
}
|
|
}
|
|
|
|
m_pvpMercenaryFaction.set(static_cast<uint32>(factionId));
|
|
m_pvpMercenaryType.set(((PvpData::isNeutralFactionId(factionId)) ? PvpType_Neutral : static_cast<int>(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<TangibleObject const *>(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 != nullptr && owner->asCreatureObject() != nullptr)
|
|
{
|
|
PlayerObject * player = PlayerCreatureController::getPlayerObject(
|
|
owner->asCreatureObject());
|
|
if (player != nullptr && 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<TangibleObject*>(reinterpret_cast<const TangibleObject*>(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<ServerObject*>((*i).getObject());
|
|
if (content && !dynamic_cast<CreatureObject*>(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<ServerObject*>((*i).getObject());
|
|
if (!isEmpty(*content))
|
|
{
|
|
|
|
if (transferer && transferer->getClient())
|
|
ContainerInterface::sendContainerMessageToClient(*transferer, Container::CEC_HopperNotEmpty);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (destination != nullptr && 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() != nullptr)
|
|
{
|
|
// 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 nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
Footprint const *TangibleObject::getFootprint() const
|
|
{
|
|
CollisionProperty const *const property = getCollisionProperty();
|
|
if (property)
|
|
return property->getFootprint();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* 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 = nullptr;
|
|
ServerObject * sourceObject = safe_cast<ServerObject *>(NetworkIdManager::getObjectById(source));
|
|
if (source != NetworkId::cms_invalid && sourceObject != nullptr)
|
|
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 != nullptr)
|
|
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 != nullptr)
|
|
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::pair<int, NetworkId> >(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<AttribMod::AttribMod>::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 nullptr\n",getNetworkId().getValueString().c_str()));
|
|
Vector const &position = topmostContainer->getPosition_p();
|
|
|
|
UpdateObjectOnPlanetMessage const msg(
|
|
getNetworkId(),
|
|
topmostContainer == this ? NetworkId::cms_invalid : topmostContainer->getNetworkId(),
|
|
static_cast<int>(position.x),
|
|
static_cast<int>(position.y),
|
|
static_cast<int>(position.z),
|
|
getInterestRadius(),
|
|
static_cast<int> (getLocationReservationRadius()),
|
|
false,
|
|
false,
|
|
static_cast<int>(getObjectType()),
|
|
0,
|
|
false,
|
|
getTemplateCrc(),
|
|
-1,
|
|
-1);
|
|
GameServer::getInstance().sendToPlanetServer(msg);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Clears our damage list.
|
|
*/
|
|
void TangibleObject::clearDamageList(void)
|
|
{
|
|
if (m_combatData != nullptr)
|
|
{
|
|
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::pair<NetworkId, float> >(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::pair<NetworkId, std::pair<int, float> > >(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::pair<NetworkId, float> >(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<NetworkId>(target));
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void TangibleObject::clearHateList()
|
|
{
|
|
if (isAuthoritative())
|
|
{
|
|
m_hateList.clear();
|
|
scheduleForAlter();
|
|
}
|
|
else
|
|
{
|
|
sendControllerMessageToAuthServer(CM_clearHateList, nullptr);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
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 != nullptr)
|
|
{
|
|
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, nullptr);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void TangibleObject::setHateListAutoExpireTargetEnabled(bool const enabled)
|
|
{
|
|
m_hateList.setAutoExpireTargetEnabled(enabled);
|
|
|
|
if (!isAuthoritative())
|
|
{
|
|
sendControllerMessageToAuthServer(CM_autoExpireHateListTargetEnabled, new MessageQueueGenericValueType<bool>(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 != nullptr)
|
|
{
|
|
const ManufactureSchematicObject * schematic = dynamic_cast<
|
|
const ManufactureSchematicObject *>(object);
|
|
if (schematic != nullptr)
|
|
m_sourceDraftSchematic = schematic->getDraftSchematic();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendControllerMessageToAuthServer(CM_setCraftedId,
|
|
new MessageQueueGenericValueType<NetworkId>(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<int>(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<TangibleObject*>(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<NetworkId>(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<int>(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<int>(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 == nullptr)
|
|
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 == nullptr)
|
|
{
|
|
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<int> goodSchematics;
|
|
std::vector<float> scriptComplexities;
|
|
const std::map<std::pair<uint32, uint32>,int> & schematics = crafter.getDraftSchematics();
|
|
std::map<std::pair<uint32, uint32>,int>::const_iterator iter;
|
|
for (iter = schematics.begin(); iter != schematics.end(); ++iter)
|
|
{
|
|
const DraftSchematicObject * schematic = DraftSchematicObject::getSchematic((*iter).first.first);
|
|
if (schematic != nullptr && ((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<uint32> empty;
|
|
crafterPlayer->sendUseableDraftSchematics (empty);
|
|
crafterPlayer->stopCrafting(true);
|
|
|
|
return false;
|
|
}
|
|
setObjVarItem(OBJVAR_CRAFTING_CRAFTER, crafter.getNetworkId());
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint32> 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<NetworkId>(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 == nullptr)
|
|
return false;
|
|
|
|
Object const * const ingredient = CachedNetworkId(ingredientId).getObject();
|
|
if (ingredient == nullptr)
|
|
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 nullptr;
|
|
}
|
|
|
|
// get the ingredient hopper
|
|
SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this);
|
|
if (container == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Crafting tool %s does not have a container!", myIdString));
|
|
return nullptr;
|
|
}
|
|
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 nullptr;
|
|
}
|
|
ServerObject * hopper = safe_cast<ServerObject *>(hopperId.getObject());
|
|
if (hopper == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Can't find object for ingredient hopper id %s",
|
|
hopperId.getValueString().c_str()));
|
|
return nullptr;
|
|
}
|
|
|
|
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 == nullptr)
|
|
{
|
|
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 == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("addObjectToOutputSlot, nullptr 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 == nullptr)
|
|
{
|
|
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 == nullptr)
|
|
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<int>(creaturePlayer->getCraftingStage())));
|
|
return false;
|
|
}
|
|
|
|
// if the object is our prototype, clear the prototype
|
|
if (getSynchronizedUi() != nullptr)
|
|
{
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync != nullptr)
|
|
{
|
|
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, nullptr, 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::pair<NetworkId, NetworkId> >(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 nullptr;
|
|
}
|
|
|
|
SlottedContainer const * const container = ContainerInterface::getSlottedContainer(*this);
|
|
if (container)
|
|
{
|
|
Container::ContainerErrorCode error;
|
|
const CachedNetworkId & schematicId =
|
|
container->getObjectInSlot(getCraftingManufactureSchematicSlotId(), error);
|
|
return safe_cast<ManufactureSchematicObject *>(schematicId.getObject());
|
|
}
|
|
else
|
|
{
|
|
DEBUG_WARNING(true, ("ManufactureSchematicObject doesn't have a slotted container."));
|
|
return nullptr;
|
|
}
|
|
} // 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() == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Tried to get non-crafting tool "
|
|
"object %s", myIdString));
|
|
return nullptr;
|
|
}
|
|
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync == nullptr)
|
|
return nullptr;
|
|
|
|
ManufactureSchematicObject * schematic = safe_cast<ManufactureSchematicObject *>(
|
|
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() == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Tried to get non-crafting tool "
|
|
"object %s", myIdString));
|
|
return;
|
|
}
|
|
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync == nullptr)
|
|
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<ServerObject *>(schematicId.getObject());
|
|
if (object == nullptr)
|
|
{
|
|
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 = nullptr;
|
|
NetworkId crafterId;
|
|
if (getObjVars().getItem(OBJVAR_CRAFTING_CRAFTER,crafterId))
|
|
{
|
|
crafter = safe_cast<ServerObject *>(NetworkIdManager::getObjectById(crafterId));
|
|
}
|
|
if (crafter != nullptr)
|
|
{
|
|
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 != nullptr && player->getCraftingStation().getObject() != nullptr)
|
|
{
|
|
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<NetworkId>(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 = nullptr;
|
|
if (getSynchronizedUi() != nullptr)
|
|
sync = dynamic_cast<CraftingToolSyncUi *>(getSynchronizedUi());
|
|
|
|
if (schematicId == CachedNetworkId::cms_cachedInvalid && sync == nullptr)
|
|
{
|
|
// no schematic
|
|
return;
|
|
}
|
|
|
|
if (schematicId == CachedNetworkId::cms_invalid)
|
|
schematicId = sync->getManfSchematic();
|
|
else if (sync != nullptr && 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() != nullptr)
|
|
{
|
|
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 = nullptr;
|
|
ServerObject * object = safe_cast<ServerObject *>(schematicId.getObject());
|
|
if (object == nullptr && 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<ManufactureSchematicObject *>(object);
|
|
|
|
if (manfSchematic != nullptr)
|
|
{
|
|
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<CreatureObject *>(NetworkIdManager::getObjectById(crafterId));
|
|
PlayerObject * const crafterPlayer = PlayerCreatureController::getPlayerObject(crafter);
|
|
if (!manfSchematic->mustDestroyIngredients() || (crafterPlayer != nullptr &&
|
|
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 != nullptr)
|
|
sync->setManfSchematic(CachedNetworkId::cms_cachedInvalid, CachedNetworkId::cms_cachedInvalid, true);
|
|
if (object != nullptr)
|
|
{
|
|
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() == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Tried to get non-crafting tool "
|
|
"object %s", myIdString));
|
|
return nullptr;
|
|
}
|
|
|
|
const CraftingToolSyncUi * sync = dynamic_cast<const CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync == nullptr)
|
|
return nullptr;
|
|
|
|
return safe_cast<ServerObject *>(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() == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Tried to get non-crafting tool "
|
|
"object %s", myIdString));
|
|
return;
|
|
}
|
|
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync == nullptr)
|
|
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<ServerObject *>(prototypeId.getObject());
|
|
if (object == nullptr)
|
|
{
|
|
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(), nullptr, 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() != nullptr)
|
|
{
|
|
safe_cast<ServerObject*>(oldItem.getObject())->permanentlyDestroy(
|
|
DeleteReasons::Consumed);
|
|
// try the transfer again
|
|
if (ContainerInterface::transferItemToSlottedContainer(*this,
|
|
prototype, getCraftingPrototypeSlotId(), nullptr, 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<NetworkId>(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() == nullptr)
|
|
{
|
|
DEBUG_WARNING(true, ("Tried to get non-crafting tool "
|
|
"object %s", myIdString));
|
|
return;
|
|
}
|
|
|
|
CraftingToolSyncUi * sync = dynamic_cast<CraftingToolSyncUi *>(
|
|
getSynchronizedUi());
|
|
if (sync == nullptr)
|
|
return;
|
|
|
|
const CachedNetworkId & prototypeId = sync->getPrototype();
|
|
ServerObject * object = safe_cast<ServerObject *>(prototypeId.getObject());
|
|
if (object != nullptr)
|
|
{
|
|
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() != nullptr)
|
|
{
|
|
safe_cast<ServerObject *>(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<int> & 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<std::vector<int> >(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<TangibleController *>(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 != nullptr)
|
|
{
|
|
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<int>(condition));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void TangibleObject::clearCondition(int condition)
|
|
{
|
|
if (isAuthoritative())
|
|
m_condition = (m_condition.get() & (~condition));
|
|
else
|
|
sendControllerMessageToAuthServer(CM_clearCondition, new MessageQueueGenericValueType<int>(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 != nullptr)
|
|
{
|
|
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<bool>(isVisible));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void TangibleObject::setHidden(bool const hide)
|
|
{
|
|
if (isAuthoritative())
|
|
m_hideFromClient = hide;
|
|
else
|
|
sendControllerMessageToAuthServer(CM_hideFromClient, new MessageQueueGenericValueType<bool>(hide));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void TangibleObject::visibilityDataModified()
|
|
{
|
|
if (isInWorld())
|
|
{
|
|
if (isVisible() && !isHidden())
|
|
{
|
|
// show the object
|
|
const TriggerVolume * triggerVolume = getTriggerVolume(NetworkTriggerVolumeNamespace::NetworkTriggerVolumeName);
|
|
if (triggerVolume != nullptr)
|
|
{
|
|
std::vector<ServerObject *> 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 != nullptr)
|
|
{
|
|
std::vector<ServerObject *> possibleObservers;
|
|
ServerWorld::findPlayerCreaturesInRange(getPosition_w(), triggerVolume->getRadius(), possibleObservers);
|
|
if (!possibleObservers.empty())
|
|
{
|
|
std::vector<ServerObject *> allowedObservers;
|
|
for (std::vector<ServerObject *>::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<ServerObject const *>(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<ServerObject const *>(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<const CreatureObject *>(ownerId.getObject());
|
|
if (owner != nullptr)
|
|
{
|
|
const GroupObject * group = owner->getGroup();
|
|
if (group != nullptr)
|
|
{
|
|
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<int> & 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 == nullptr)
|
|
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<float> 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<int>::iterator j = encumbrances.begin();
|
|
for (std::vector<float>::const_iterator i = encumbranceSplit.begin(); i != encumbranceSplit.end(); ++i, ++j)
|
|
{
|
|
*j = static_cast<int>((*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<char>(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<std::pair<int, int> > & 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<int> & attribBonuses) const
|
|
{
|
|
attribBonuses.clear();
|
|
attribBonuses.resize(Attributes::NumberOfAttributes, 0);
|
|
|
|
bool hasBonues = false;
|
|
typedef std::vector<std::pair<int, int> > 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<std::pair<std::string, int> > & skillModBonuses, bool includeCategorizedSkillmod /*= true*/) const
|
|
{
|
|
static std::map<std::string, int> 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<std::string, int>::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<std::string, int>::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<const char *> skillModParam;
|
|
skillModParam.push_back(skillMod.c_str());
|
|
std::vector<int> 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<std::pair<std::string,
|
|
int> > & skillModBonuses)
|
|
{
|
|
int sockets = getSkillModSockets();
|
|
if (sockets > 0)
|
|
{
|
|
int i;
|
|
int count = skillModBonuses.size();
|
|
std::vector<int> bonusOrder(1, 0);
|
|
for (i = 1; i < count; ++i)
|
|
{
|
|
std::vector<int>::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<int>(ServerClock::getInstance().getGameTimeSeconds());
|
|
timeLeft = static_cast<int>(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 != nullptr)
|
|
{
|
|
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<const ServerObjectTemplate *>(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<std::pair<std::string, int> > 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<int> 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<ServerObject const *>(ContainerInterface::getContainedByObject(*this));
|
|
if (cell && cell->asCellObject())
|
|
{
|
|
ServerObject const * const shipAsObject = safe_cast<ServerObject const *>(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<GameScriptObject *>(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<int>(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<int>(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<int>(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<int>(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<int>(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<NetworkId, LfgCharacterData> const & connectedCharacterLfgData = ServerUniverse::getConnectedCharacterLfgData();
|
|
std::map<NetworkId, LfgCharacterData>::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 != nullptr)
|
|
regionName = Unicode::wideToNarrow(region->getName());
|
|
|
|
int const cityId = CityInterface::getCityAtLocation(sceneId, static_cast<int>(position.x), static_cast<int>(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<NetworkId> expired;
|
|
unsigned long const currentGameTime = ServerClock::getInstance().getGameTimeSeconds();
|
|
|
|
for (std::map<NetworkId, std::pair<float, std::pair<unsigned long, unsigned long> > >::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<float>(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<NetworkId>::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<CustomizationDataProperty *>(getProperty (CustomizationDataProperty::getClassPropertyId()));
|
|
return cdprop ? cdprop->fetchCustomizationData () : 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
const CustomizationData *TangibleObject::fetchCustomizationData () const
|
|
{
|
|
const CustomizationDataProperty * const cdprop = safe_cast<const CustomizationDataProperty *>(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<ServerObjectTemplate const *>(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 != nullptr)
|
|
{
|
|
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<int>((safe_cast<const ServerTangibleObjectTemplate *>(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<const SharedTangibleObjectTemplate*> (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<Unicode::String>::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<Unicode::String>::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() != nullptr), getPvpFaction());
|
|
bool const isPvpSync = PvpUpdateObserver::satisfyPvpSyncCondition(!pvpable, hasCondition(ServerTangibleObjectTemplate::C_invulnerable), (asCreatureObject() != nullptr), getPvpFaction());
|
|
|
|
if (wasPvpSync != isPvpSync)
|
|
{
|
|
// force pvp status update
|
|
Pvp::forceStatusUpdate(*this);
|
|
|
|
const std::set<Client *> &observers = getObservers();
|
|
for (std::set<Client *>::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<int>(getPvpRegionCheckTime()));
|
|
else if (!pvpable && oldPvpable)
|
|
MessageToQueue::cancelRecurringMessageTo(getNetworkId(), "C++CheckPvpRegion");
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int TangibleObject::getInterestRadius() const
|
|
{
|
|
if (isPlayerControlled())
|
|
return ConfigServerGame::getPlayerInterestRange();
|
|
ServerTangibleObjectTemplate const *myTemplate = safe_cast<ServerTangibleObjectTemplate const *>(getObjectTemplate());
|
|
return myTemplate->getInterestRadius();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void TangibleObject::getRequiredCertifications(std::vector<std::string> & results) const
|
|
{
|
|
const SharedTangibleObjectTemplate *const sharedObjectTemplate = safe_cast<const SharedTangibleObjectTemplate*>(getSharedTemplate());
|
|
if (sharedObjectTemplate)
|
|
{
|
|
const int numRequired = sharedObjectTemplate->getCertificationsRequiredCount();
|
|
for(int i=0; i<numRequired; ++i)
|
|
{
|
|
results.push_back(sharedObjectTemplate->getCertificationsRequired(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() : nullptr;
|
|
TangibleObject * const tangibleContainer = serverContainer ? serverContainer->asTangibleObject() : nullptr;
|
|
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<uint32>(proxyList.begin(), proxyList.end()));
|
|
|
|
GenericValueTypeMessage<std::pair<NetworkId, std::string> > 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<NetworkId>(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>(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<TangibleObject *>(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<int>(priority), fromServer));
|
|
}
|
|
else
|
|
{
|
|
CommandQueue * const queue = getCommandQueue();
|
|
if (queue != nullptr)
|
|
{
|
|
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<int>(Command::CEC_Success), 0));
|
|
}
|
|
else
|
|
{
|
|
CommandQueue * const queue = getCommandQueue();
|
|
if (queue != nullptr)
|
|
{
|
|
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 != nullptr)
|
|
{
|
|
return queue->hasCommandFromGroup(groupHash);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void TangibleObject::commandQueueClearCommandsFromGroup(uint32 groupHash, bool force)
|
|
{
|
|
CommandQueue * const queue = getCommandQueue();
|
|
if (queue != nullptr)
|
|
{
|
|
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 != nullptr)
|
|
{
|
|
aiCreatureController->markCombatStartLocation();
|
|
}
|
|
|
|
if (target.getScriptObject() != nullptr)
|
|
{
|
|
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() != nullptr)
|
|
{
|
|
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<int>(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<bool>(attackable));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool TangibleObject::hasAutoDeclineDuel() const
|
|
{
|
|
return getObjVars().hasItem(OBJVAR_DECLINE_DUEL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int TangibleObject::getPassiveRevealRange(NetworkId const & target) const
|
|
{
|
|
Archive::AutoDeltaMap<NetworkId, int>::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, (nullptr != 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<std::pair<std::pair<int, int>, 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<NetworkId>(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<NetworkId>(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<int, NetworkId> >(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<int, NetworkId> >(std::make_pair(guildId, NetworkId::cms_invalid) ) );
|
|
}
|
|
}
|
|
|
|
|
|
void TangibleObject::removeUserFromAccessList(const NetworkId user)
|
|
{
|
|
if(isAuthoritative())
|
|
{
|
|
Archive::AutoDeltaSet<NetworkId>::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<int, NetworkId> >(std::make_pair(-1, user)));
|
|
}
|
|
}
|
|
|
|
void TangibleObject::removeGuildFromAccessList(int guildId)
|
|
{
|
|
if(isAuthoritative())
|
|
{
|
|
Archive::AutoDeltaSet<int>::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<int, NetworkId> >(std::make_pair(guildId, NetworkId::cms_invalid)));
|
|
}
|
|
}
|
|
|
|
void TangibleObject::copyUserAccessListToObjVars()
|
|
{
|
|
std::vector<NetworkId> newAccessList;
|
|
Archive::AutoDeltaSet<NetworkId>::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<int> newAccessList;
|
|
Archive::AutoDeltaSet<int>::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<NetworkId> inputVector;
|
|
if(getObjVars().getItem(s_accessListScriptVar, inputVector))
|
|
{
|
|
// We have some obj vars to add to our list!
|
|
std::vector<NetworkId>::size_type i = 0;
|
|
for( ; i < inputVector.size(); ++i)
|
|
{
|
|
m_accessList.insert(inputVector[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TangibleObject::readInGuildAccessListObjVars()
|
|
{
|
|
std::vector<int> inputVector;
|
|
if(getObjVars().getItem(s_guildAccessListScriptVar, inputVector))
|
|
{
|
|
// We have some obj vars to add to our list!
|
|
std::vector<int>::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<bool>(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<bool>(true));
|
|
}
|
|
}
|
|
|
|
void TangibleObject::getUserAccessList(std::vector<NetworkId> & ids)
|
|
{
|
|
getObjVars().getItem(s_accessListScriptVar, ids);
|
|
}
|
|
|
|
void TangibleObject::getGuildAccessList(std::vector<int> & 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::pair<std::string, std::pair<std::string, std::pair<std::string, std::pair<Vector, float > > > > >(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<std::string, std::pair<std::string, std::pair<std::string, std::pair<Vector, float> > > >::const_iterator iter = m_effectsMap.find(label);
|
|
if(iter == m_effectsMap.end())
|
|
return;
|
|
|
|
m_effectsMap.erase(iter);
|
|
}
|
|
else
|
|
sendControllerMessageToAuthServer(CM_removeObjectEffect, new MessageQueueGenericValueType<std::string>(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<bool>(true));
|
|
}
|
|
// ======================================================================
|