Files
src-1.2/engine/server/library/serverGame/src/shared/object/ManufactureSchematicObject.cpp
T

3470 lines
115 KiB
C++
Executable File

//========================================================================
//
// ManufactureSchematicObject.cpp
//
// copyright 2001 Sony Online Entertainment
//
//========================================================================
#include "serverGame/FirstServerGame.h"
#include "serverGame/ManufactureSchematicObject.h"
#include "UnicodeUtils.h"
#include "serverGame/Chat.h"
#include "serverGame/ConfigServerGame.h"
#include "serverGame/ContainerInterface.h"
#include "serverGame/CreatureObject.h"
#include "serverGame/DraftSchematicObject.h"
#include "serverGame/FactoryObject.h"
#include "serverGame/GameServer.h"
#include "serverGame/ManufactureSchematicSynchronizedUi.h"
#include "serverGame/PlayerCreatureController.h"
#include "serverGame/PlayerObject.h"
#include "serverGame/ProsePackageManagerServer.h"
#include "serverGame/ResourceContainerObject.h"
#include "serverGame/ResourceTypeObject.h"
#include "serverGame/ServerController.h"
#include "serverGame/ServerManufactureSchematicObjectTemplate.h"
#include "serverGame/ServerMessageForwarding.h"
#include "serverGame/ServerUniverse.h"
#include "serverGame/ServerWorld.h"
#include "serverScript/GameScriptObject.h"
#include "serverScript/ScriptParameters.h"
#include "sharedFoundation/ConstCharCrcLowerString.h"
#include "sharedFoundation/Crc.h"
#include "sharedFoundation/DynamicVariableList.h"
#include "sharedGame/CraftingData.h"
#include "sharedGame/OutOfBandPackager.h"
#include "sharedGame/ProsePackage.h"
#include "sharedGame/SharedObjectAttributes.h"
#include "sharedLog/Log.h"
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
#include "sharedNetworkMessages/MessageQueueDraftSlots.h"
#include "sharedNetworkMessages/MessageQueueDraftSlotsQueryResponse.h"
#include "sharedObject/ArrangementDescriptor.h"
#include "sharedObject/ArrangementDescriptorList.h"
#include "sharedObject/CustomizationData.h"
#include "sharedObject/CustomizationDataProperty.h"
#include "sharedObject/NetworkIdManager.h"
#include "sharedObject/ObjectTemplateList.h"
#include "sharedObject/RangedIntCustomizationVariable.h"
#include "sharedObject/SlotIdManager.h"
#include "sharedObject/SlottedContainmentProperty.h"
#include "sharedObject/VolumeContainer.h"
#include "sharedUtility/DataTable.h"
#include "sharedUtility/DataTableManager.h"
namespace ManufactureSchematicObjectNamespace
{
// objvar names
const std::string OBJVAR_CRAFTING_IS_PROTOTYPE("crafting.isPrototype");
const std::string OBJVAR_SLOTS("ingr");
const std::string OBJVAR_SLOT_NAME("name");
const std::string OBJVAR_SLOT_INDEX("index");
const std::string OBJVAR_SLOT_TYPE("type");
const std::string OBJVAR_SLOT_OPTION("option");
const std::string OBJVAR_SLOT_COMPLEXITY("complex");
const std::string OBJVAR_INGREDIENTS("ingr");
const std::string OBJVAR_RESOURCE_ID("id");
const std::string OBJVAR_INGR_RESOURCE_ID(OBJVAR_INGREDIENTS + '.' + OBJVAR_RESOURCE_ID);
const std::string OBJVAR_RESOURCE_IMPORTED_DATA("resourceData");
const std::string OBJVAR_INGR_RESOURCE_IMPORTED_DATA(OBJVAR_INGREDIENTS + '.' + OBJVAR_RESOURCE_IMPORTED_DATA);
const std::string OBJVAR_RESOURCE_COUNT("cnt");
const std::string OBJVAR_COMPONENT_NAME("name");
const std::string OBJVAR_COMPONENT_ID("realId");
const std::string OBJVAR_COMPONENT_CRAFTED_ID("id");
const std::string OBJVAR_COMPONENT_CRAFTED_XP_TYPE("xpType");
const std::string OBJVAR_COMPONENT_TEMPLATE_NAME("tpl");
const std::string OBJVAR_SLOT_SOURCES("src");
const std::string OBJVAR_ATTRIBUTES("crafting_attributes");
const std::string OBJVAR_COMPONENTS("crafting_components");
const std::string OBJVAR_STORED_RESOURCES("crafting_resource");
const std::string OBJVAR_ATTRIBUTE_BONUSES("attrib_bonuses");
const std::string OBJVAR_ATTRIBUTE_BONUS("attribute.bonus");
const std::string OBJVAR_INVALID_SCHEMATIC("ImAnInvalidSchematic");
const std::string OBJVAR_ORIGINAL_OBJECT_ID("original_object_id");
const StringId ATTRIBUTES_COMPLEXITY("crafting","complexity");
// this is a special schematic that we use to show an image for schematics
// that have been removed from the game
const std::string MISSING_SCHEMATIC_SUBSTITUTE("object/draft_schematic/base/missing_schematic_substitute.iff");
}
using namespace ManufactureSchematicObjectNamespace;
//----------------------------------------------------------------------
const SharedObjectTemplate * ManufactureSchematicObject::m_defaultSharedTemplate = nullptr;
//========================================================================
/**
* Class constructor. This will only get called when making a schematic created
* by a designer.
*
* @param newTemplate template to create the schematic from.
*/
ManufactureSchematicObject::ManufactureSchematicObject(
const ServerManufactureSchematicObjectTemplate* newTemplate) :
IntangibleObject(newTemplate),
m_draftSchematicSharedTemplate(),
m_draftSchematic(),
m_creatorId(NetworkId::cms_invalid),
m_creatorName(Unicode::narrowToWide(newTemplate->getCreator())),
m_itemsPerContainer(1),
m_attributes(),
m_manufactureTime(1),
m_isCrafting(false),
m_customAppearance(),
m_appearanceData(),
m_resourceMaxAttributes(),
m_schematicChangedSignal(0),
m_factories(),
m_isMakingObject(false)
{
addMembersToPackages();
} // ManufactureSchematicObject::ManufactureSchematicObject(designer made)
//-----------------------------------------------------------------------
/**
* Class destructor.
*/
ManufactureSchematicObject::~ManufactureSchematicObject()
{
// m_synchronizedUi deleted by superclass
} // ManufactureSchematicObject::~ManufactureSchematicObject
//-----------------------------------------------------------------------
ManufactureSchematicObject * ManufactureSchematicObject::asManufactureSchematicObject()
{
return this;
}
//-----------------------------------------------------------------------
ManufactureSchematicObject const * ManufactureSchematicObject::asManufactureSchematicObject() const
{
return this;
}
//-----------------------------------------------------------------------
void ManufactureSchematicObject::kill()
{
IntangibleObject::kill();
}
//-----------------------------------------------------------------------
/**
* Returns a shared template if none was given for this object.
*
* @return the shared template
*/
const SharedObjectTemplate * ManufactureSchematicObject::getDefaultSharedTemplate() const
{
static const ConstCharCrcLowerString templateName("object/manufacture_schematic/base/shared_manufacture_schematic_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, "ManufactureSchematicObject::removeDefaultTemplate");
}
return m_defaultSharedTemplate;
} // ManufactureSchematicObject::getDefaultSharedTemplate
//-----------------------------------------------------------------------
/**
* Cleans up the default shared template.
*/
void ManufactureSchematicObject::removeDefaultTemplate()
{
if (m_defaultSharedTemplate != nullptr)
{
m_defaultSharedTemplate->releaseReference();
m_defaultSharedTemplate = nullptr;
}
} // ManufactureSchematicObject::removeDefaultTemplate
//-----------------------------------------------------------------------
/**
* Initializes the schematic from a draft schematic being used at a crafting
* station.
*
* @param schematic the draft schematic used to make this schematic
* @param creator the player crafting
*/
void ManufactureSchematicObject::init(const DraftSchematicObject & schematic, const CachedNetworkId & creator)
{
m_draftSchematic = schematic.getObjectTemplate()->getCrcName().getCrc();
m_draftSchematicSharedTemplate = schematic.getSharedTemplate()->getCrcName().getCrc();
m_creatorId = creator;
if (creator.getObject() != nullptr)
{
m_creatorName = safe_cast<const ServerObject *>(creator.getObject())->
getObjectName();
}
m_itemsPerContainer = schematic.getItemsPerContainer();
m_manufactureTime = schematic.getManufactureTime();
m_attributes.clear();
m_resourceMaxAttributes.clear();
if (isAuthoritative())
removeObjVarItem(".");
m_isCrafting = true;
} // ManufactureSchematicObject::init
//-----------------------------------------------------------------------
/**
* Initializes data for a newly created object.
*/
void ManufactureSchematicObject::initializeFirstTimeObject()
{
IntangibleObject::initializeFirstTimeObject();
const DraftSchematicObject * const sourceSchematic = DraftSchematicObject::getSchematic(getDraftSchematic());
NOT_NULL(sourceSchematic);
const ServerDraftSchematicObjectTemplate * const sourceSchematicTemplate =
safe_cast<const ServerDraftSchematicObjectTemplate *>(sourceSchematic->getObjectTemplate());
NOT_NULL(sourceSchematicTemplate);
setObjectName (sourceSchematic->getAssignedObjectName ());
setObjectNameStringId (sourceSchematic->getObjectNameStringId ());
setCount(10);
// the slots need to be set up externally, filtered by player skill
eraseObjVarListEntries(OBJVAR_SLOTS);
computeComplexity();
// copy the draft schematic objvars onto us
{
DynamicVariableList objvars;
sourceSchematicTemplate->getObjvars(objvars);
for (DynamicVariableList::MapType::const_iterator i = objvars.begin();
i != objvars.end(); ++i)
{
setObjVarItem((*i).first, (*i).second);
}
}
{
const int count = sourceSchematicTemplate->getManufactureScriptsCount();
GameScriptObject * const scriptObject = getScriptObject();
NOT_NULL(scriptObject);
for (int i = 0; i < count; ++i)
{
scriptObject->attachScript(sourceSchematicTemplate->getManufactureScripts(i), true);
}
}
} // ManufactureSchematicObject::initializeFirstTimeObject
//-----------------------------------------------------------------------
/**
* Initializes an object just proxied.
*/
void ManufactureSchematicObject::endBaselines()
{
IntangibleObject::endBaselines();
recalculateData();
} // ManufactureSchematicObject::endBaselines
//-----------------------------------------------------------------------
/**
* Creates a synchronized ui class in order to get data to the client during a
* crafting session.
*
* @return the newly created class
*/
ServerSynchronizedUi * ManufactureSchematicObject::createSynchronizedUi ()
{
ManufactureSchematicSynchronizedUi * sync = new ManufactureSchematicSynchronizedUi(*this);
// set up the sync ui for our slots
int i;
int count;
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
{
Crafting::IngredientSlot slotData;
count = slots.getCount();
for (i = 0; i < count; ++i)
{
if (getSlot(i, slotData, false))
{
sync->createNewSlot(slotData.name, slotData.complexity);
sync->setSlotType(slotData.name, slotData.ingredientType);
sync->setSlotOption(slotData.name, slotData.draftSlotOption);
sync->setSlotIndex(slotData.name, slotData.draftSlotIndex);
}
}
}
count = draft->getExperimentalAttributesCount();
for (i = 0; i < count; ++i)
{
sync->setAttribute(draft->getExperimentalAttribute(i), 0);
}
sync->setReady();
return sync;
}
//------------------------------------------------------------------------------
/**
* Re-initializes the schematic.
*/
void ManufactureSchematicObject::reset()
{
const DraftSchematicObject * const schematic = DraftSchematicObject::getSchematic(m_draftSchematic.get());
NOT_NULL(schematic);
const CreatureObject * owner = NetworkIdManager::getObjectById(m_creatorId.get())->asServerObject()->asCreatureObject();
NOT_NULL(owner);
init(*schematic, CachedNetworkId(*owner));
computeComplexity();
setObjVarItem(OBJVAR_CRAFTING_IS_PROTOTYPE, true);
Crafting::IngredientSlot manfSlot;
ServerDraftSchematicObjectTemplate::IngredientSlot slot;
const int slotCount = schematic->getSlotsCount();
for (int i = 0; i < slotCount; ++i)
{
if (!schematic->getSlot(slot, i))
{
DEBUG_WARNING(true, ("draft schematic slot %d for schematic %s not "
"found!",i, schematic->getObjectTemplateName()));
continue;
}
if (!slot.optional || slot.optionalSkillCommand.empty() ||
owner->hasCommand(slot.optionalSkillCommand))
{
// create the slot
IGNORE_RETURN(getSlot(slot.name, manfSlot));
// set the manufacturing slot->draft slot map
setSlotIndex(slot.name, i);
}
}
} // ManufactureSchematicObject::reset
//------------------------------------------------------------------------------
/**
* Returns the draft schematic name crc for the schematic used to define this
* schematic.
*
* @return the schematic crc
*/
uint32 ManufactureSchematicObject::getDraftSchematic() const
{
return m_draftSchematic.get();
}
//------------------------------------------------------------------------------
/**
* Returns if we are creating a finished item or a prototype.
*
* @return true if we are making a finished item, false if a prototype
*/
bool ManufactureSchematicObject::isMakingObject() const
{
return m_isMakingObject;
}
//------------------------------------------------------------------------------
/**
* Returns our network id. Needed so that the ManufactureObjectInterface can
* report its id.
*
* @return our network id
*/
const NetworkId & ManufactureSchematicObject::getDerivedNetworkId() const
{
return getNetworkId();
} // ManufactureSchematicObject::getDerivedNetworkId
//------------------------------------------------------------------------------
/**
* Sets the value for an attribute of the schematic
*
* @param attribute the attribute data
*/
void ManufactureSchematicObject::setAttribute(const ServerIntangibleObjectTemplate::SchematicAttribute & attribute)
{
setAttribute(attribute.name, static_cast<float>(attribute.value));
}
//------------------------------------------------------------------------------
/**
* Sets the value for an attribute of the schematic
*
* @param name the attribute name
* @param value the attribute value
*/
void ManufactureSchematicObject::setAttribute(const StringId & name, float value)
{
if (isAuthoritative())
{
m_attributes.set(name, value);
}
else
{
WARNING_STRICT_FATAL(true, ("ManufactureSchematicObject::setAttribute "
"called on non-auth object %s", getNetworkId().getValueString().c_str()));
}
}
//-----------------------------------------------------------------------
const std::map<StringId, float> & ManufactureSchematicObject::getResourceMaxAttributes() const
{
return m_resourceMaxAttributes.getMap();
}
//-----------------------------------------------------------------------
void ManufactureSchematicObject::setResourceMaxAttribute(const StringId & name, float value)
{
m_resourceMaxAttributes.set(name, value);
}
//-----------------------------------------------------------------------
/**
* Returns the crafting type of this schematic.
*
* @return the crafting type
*/
ServerIntangibleObjectTemplate::CraftingType ManufactureSchematicObject::getCategory() const
{
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
getDraftSchematic());
// There's a possibility that the draft schematic this schematic is based on
// has been changed enough to make this schematic useless, or has been removed
// from the game. If this happens, we log it and tell the player to delete
// this schematic
static const int BUFSIZE = 1024;
char errBuffer[BUFSIZE];
bool schematicOk = true;
if (getObjVars().hasItem(OBJVAR_INVALID_SCHEMATIC))
{
schematicOk = false;
}
else if (draft != nullptr)
{
// test the ingredients
Crafting::IngredientSlot sourceSlot;
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
int numSlots = getSlotsCount();
for (int i = 0; i < numSlots; ++i)
{
// see if we can get the slot from ourself
if (!getSlot(i, sourceSlot, true))
{
sprintf(errBuffer, "slot # %d not existing", i);
schematicOk = false;
break;
}
// see if the draft schematic knows about the slot
if (!draft->getSlot(draftSlot, sourceSlot.name))
{
snprintf(errBuffer, BUFSIZE, "missing slot %s in draft schematic %s",
sourceSlot.name.getCanonicalRepresentation().c_str(),
draft->getObjectTemplateName());
errBuffer[BUFSIZE-1] = '\0';
schematicOk = false;
break;
}
}
}
else
{
sprintf(errBuffer, "its source draft schematic (crc=%lu) not existing",
getDraftSchematic());
schematicOk = false;
}
if (schematicOk)
return draft->getCategory();
// this schematic can't be used, inform the player
const CreatureObject * owner = nullptr;
const Object * object = ContainerInterface::getContainedByObject(*this);
while (owner == nullptr && object != nullptr && object->asServerObject() != nullptr)
{
if (object->asServerObject()->asCreatureObject() != nullptr)
owner = object->asServerObject()->asCreatureObject();
else
object = ContainerInterface::getContainedByObject(*object);
}
// log that the schematic can't be used
LOG("CustomerService", ("Crafting:Manufacturing schematic %s "
"owned by %s is unuseable due to %s", getNetworkId().getValueString().c_str(),
PlayerObject::getAccountDescription(owner).c_str(), errBuffer));
if (owner != nullptr)
{
// send mail to the owner telling them their schematic can't be used
const static StringId message("system_msg", "manf_schematic_unuseable");
Unicode::String oob;
ProsePackage pp;
ProsePackageManagerServer::createSimpleProsePackage (*this, message, pp);
OutOfBandPackager::pack(pp, -1, oob);
Unicode::String messageString = Unicode::narrowToWide("@" + message.getCanonicalRepresentation());
Chat::sendPersistentMessage("", Unicode::wideToNarrow(owner->getObjectName()),
messageString, Unicode::emptyString, oob);
}
return static_cast<ServerIntangibleObjectTemplate::CraftingType>(0);
} // ManufactureSchematicObject::getCategory
//-----------------------------------------------------------------------
/**
* Forwards the draft schematic flag.
*
* @return true if a crafting session destroys it's ingredients
*/
bool ManufactureSchematicObject::mustDestroyIngredients() const
{
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
if (draft != nullptr)
return draft->mustDestroyIngredients();
return false;
} // ManufactureSchematicObject::mustDestroyIngredients
//-----------------------------------------------------------------------
/**
* Returns an ingredient slot needed to manufacture an item with this schematic.
*
* @param index slot to get
* @param data structure to be filled in with the slot data
* @param creatingObject flag that we are getting the slot in order to create
* a crafted object; if the slot is a component slot,
* this will cause the data.ingredient value to be the
* component id, instead of the component's manf schematic id
*
* @return true on success, false if the index was out of range
*/
bool ManufactureSchematicObject::getSlot(int index, Crafting::IngredientSlot & data,
bool creatingObject) const
{
const DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
if (index < 0 || index >= static_cast<int>(slots.getCount()))
return false;
const DynamicVariableList::NestedList slot(slots.getItemByPosition(index).getNestedList());
int intval=0;
if (slot.getItem(OBJVAR_SLOT_TYPE,intval))
data.ingredientType = static_cast<enum Crafting::IngredientType>(intval);
slot.getItem(OBJVAR_SLOT_NAME,data.name);
slot.getItem(OBJVAR_SLOT_OPTION,data.draftSlotOption);
slot.getItem(OBJVAR_SLOT_INDEX,data.draftSlotIndex);
slot.getItem(OBJVAR_SLOT_COMPLEXITY,data.complexity);
data.ingredients.clear();
// get the draft schematic and slot info for the slot to determine the
// ingredient type
const DraftSchematicObject * const draft = DraftSchematicObject::getSchematic(getDraftSchematic());
if (!draft)
{
WARNING (true, ("ManufactureSchematicObject [%d] has invalid draft "
"schematic [%u]", getNetworkId().getValueString().c_str(),
getDraftSchematic()));
return false;
}
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (!draft->getSlot(draftSlot, data.name))
return false;
ServerIntangibleObjectTemplate::IngredientType ingredientType;
if (data.draftSlotOption < 0 || data.draftSlotOption >= static_cast<int>(draftSlot.options.size()))
{
ingredientType = draftSlot.options[0].ingredientType;
}
else
ingredientType = draftSlot.options[data.draftSlotOption].ingredientType;
DynamicVariableList::NestedList slotIngredients(slot,OBJVAR_INGREDIENTS);
if (ingredientType == ServerIntangibleObjectTemplate::IT_item ||
ingredientType == ServerIntangibleObjectTemplate::IT_template ||
ingredientType == ServerIntangibleObjectTemplate::IT_templateGeneric ||
ingredientType == ServerIntangibleObjectTemplate::IT_schematic ||
ingredientType == ServerIntangibleObjectTemplate::IT_schematicGeneric)
{
// ingredient is a component
// in theory there can be more components than sources (although not the
// reverse), so we need to keep track of the sources separately
DynamicVariableList::NestedList sources(slot,OBJVAR_SLOT_SOURCES);
int sourceIndex = 0;
char buffer[32];
const int count = slotIngredients.getCount();
for (int i = 0; i < count; ++i)
{
_itoa(i, buffer, 10);
const DynamicVariableList::NestedList ingredient(slotIngredients,buffer);
Crafting::ComponentIngredient * const component = new Crafting::ComponentIngredient();
data.ingredients.push_back(Crafting::SimpleIngredientPtr(component));
component->count = 1;
if (!ingredient.hasItem(OBJVAR_COMPONENT_NAME))
return false;
ingredient.getItem(OBJVAR_COMPONENT_NAME,component->componentName);
const bool isComponentCrafted = ingredient.hasItem(OBJVAR_COMPONENT_CRAFTED_XP_TYPE);
if (isComponentCrafted)
{
// set the xp type
ingredient.getItem(OBJVAR_COMPONENT_CRAFTED_XP_TYPE,component->xpType);
// set the xp source and amount
if (!sources.empty())
{
DynamicVariableList::NestedList::const_iterator data(sources.getItemByPosition(sourceIndex));
if (data != sources.end())
{
component->source = NetworkId(data.getName());
data.getValue(component->sourceXp);
++sourceIndex;
}
}
}
if (creatingObject)
{
ingredient.getItem(OBJVAR_COMPONENT_ID, component->ingredient);
}
else
{
if (ingredientType != ServerIntangibleObjectTemplate::IT_templateGeneric && isComponentCrafted)
{
ingredient.getItem(OBJVAR_COMPONENT_CRAFTED_ID, component->ingredient);
}
else
{
if (!ingredient.getItem(OBJVAR_COMPONENT_TEMPLATE_NAME,component->templateName))
return false;
}
}
}
}
else
{
// ingredient is a resource
NetworkId ingredientId;
if (slotIngredients.getItem(OBJVAR_RESOURCE_ID,ingredientId))
{
int resourceCount = 0;
slotIngredients.getItem(OBJVAR_RESOURCE_COUNT, resourceCount);
// get the xp type based on the resource container
int xpType = 0;
const ResourceTypeObject * const resourceType = ServerUniverse::getInstance().getResourceTypeById(ingredientId);
if (resourceType != nullptr)
{
std::string crateTemplateName;
resourceType->getCrateTemplate(crateTemplateName);
const ServerObjectTemplate * crateTemplate = safe_cast<const ServerObjectTemplate *>(ObjectTemplateList::fetch(crateTemplateName));
if (crateTemplate != nullptr && crateTemplate->getXpPointsCount() > 0)
{
ServerObjectTemplate::Xp xpData;
crateTemplate->getXpPoints(xpData, 0);
xpType = xpData.type;
}
}
const DynamicVariableList::NestedList sources(slot,OBJVAR_SLOT_SOURCES);
if (!sources.empty())
{
for (DynamicVariableList::NestedList::const_iterator source=sources.begin(); source!=sources.end(); ++source)
{
const NetworkId sourceId(source.getName());
int sourceResourceCount = 0;
source.getValue(sourceResourceCount);
data.ingredients.push_back(Crafting::SimpleIngredientPtr(
new Crafting::SimpleIngredient(ingredientId, sourceResourceCount,
sourceId, xpType)));
}
}
else
{
data.ingredients.push_back(Crafting::SimpleIngredientPtr(
new Crafting::SimpleIngredient(ingredientId, resourceCount,
NetworkId::cms_invalid, xpType)));
}
}
}
return true;
} // ManufactureSchematicObject::getSlot(int)
//-----------------------------------------------------------------------
/**
* Gets the ingredient slot with a given name. If the slot does not exist (which
* may be the case for optional slots) the slot will be created.
*
* @param name the slot name
* @param data structure to be filled in with the slot data
*
* @return true on success, false on error
*/
bool ManufactureSchematicObject::getSlot(const StringId & name, Crafting::IngredientSlot & data)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
DynamicVariableList::NestedList::const_iterator i(slots.begin());
int index=0;
// see if the slot already exists
for (; i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && name == nameValue)
{
break;
}
++index;
}
if (i == slots.end())
{
// check the draft schematic for the slot
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (!draft->getSlot(draftSlot, name))
return false;
// add the obvars for the new slot
// @todo: can we make the list name the slot name? Is the StringId text unique?
char buffer[256];
char secondBuffer[32];
if(slots.getCount() < 10)
sprintf(secondBuffer, "0%d", slots.getCount());
else
sprintf(secondBuffer, "%d", slots.getCount());
sprintf(buffer, "%s.%s.", OBJVAR_SLOTS.c_str(), secondBuffer);
setObjVarItem(buffer + OBJVAR_SLOT_TYPE, Crafting::IT_none);
setObjVarItem(buffer + OBJVAR_SLOT_NAME, draftSlot.name);
setObjVarItem(buffer + OBJVAR_SLOT_OPTION, -1);
setObjVarItem(buffer + OBJVAR_SLOT_INDEX, -1);
setObjVarItem(buffer + OBJVAR_SLOT_COMPLEXITY, draftSlot.complexity);
ManufactureSchematicSynchronizedUi * const sync = dynamic_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->createNewSlot(draftSlot.name, draftSlot.complexity);
}
return getSlot(index, data, false);
} // ManufactureSchematicObject::getSlot(const StringId &)
//-----------------------------------------------------------------------
/**
* Returns the number of ingredient slots needed to manufacture an item with
* this schematic.
*
* @return the ingredient count
*/
int ManufactureSchematicObject::getSlotsCount() const
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
return slots.getCount();
} // ManufactureSchematicObject::getSlotsCount
//-----------------------------------------------------------------------
/**
* Sets the slot type of a manf slot.
*
* @param name the name of the slot
* @param type the slot type
*/
void ManufactureSchematicObject::setSlotType(const StringId & name, int type)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && name == nameValue)
{
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_TYPE, type);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setSlotType(name, type);
break;
}
}
} // ManufactureSchematicObject::setSlotType
//-----------------------------------------------------------------------
/**
* Sets the slot option index of a manf slot.
*
* @param name the name of the slot
* @param option the option index
*/
void ManufactureSchematicObject::setSlotOption(const StringId & name, int option)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot = i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name == nameValue))
{
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_OPTION, option);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setSlotOption(name, option);
break;
}
}
} // ManufactureSchematicObject::setSlotOption
//-----------------------------------------------------------------------
/**
* Sets the draft schematic slot index of a manf slot.
*
* @param name the name of the slot
* @param index the draft schematic index
*/
void ManufactureSchematicObject::setSlotIndex(const StringId & name, int index)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name==nameValue))
{
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_INDEX, index);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setSlotIndex(name, index);
break;
}
}
} // ManufactureSchematicObject::setSlotIndex
//-----------------------------------------------------------------------
/**
* Modifies a slot's complexity by a value.
*
* @param name the name of the slot
* @param complexityDelta value to add to the current slot complexity
*/
void ManufactureSchematicObject::modifySlotComplexity(const StringId & name,
float complexityDelta)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name==nameValue))
{
float complexity =0;
slot.getItem(OBJVAR_SLOT_COMPLEXITY,complexity);
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_COMPLEXITY, complexity + complexityDelta);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setSlotComplexity(name, complexity + complexityDelta);
break;
}
}
} // ManufactureSchematicObject::modifySlotComplexity
//-----------------------------------------------------------------------
/**
* Adds a resource type to a schematic slot.
*
* @param name the name of the slot
* @param resourceId id of the resource to add
* @param count number of resources to add
*/
void ManufactureSchematicObject::addSlotResource(const StringId & name,
const NetworkId & resourceId, int count, const NetworkId & sourceId)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name==nameValue))
{
DynamicVariableList::NestedList slotIngredients(slot,OBJVAR_INGREDIENTS);
setObjVarItem(slotIngredients.getNameWithPath()+'.'+OBJVAR_RESOURCE_ID, resourceId);
// if the resource is an imported resource, save the resource data as well
removeObjVarItem(slotIngredients.getNameWithPath()+'.'+OBJVAR_RESOURCE_IMPORTED_DATA);
if (resourceId.getValue() > NetworkId::cms_maxNetworkIdWithoutClusterId)
{
ResourceTypeObject const * const rto = ServerUniverse::getInstance().getImportedResourceTypeById(resourceId);
if (rto)
{
std::string const resourceData = rto->getResourceTypeDataForExport();
if (!resourceData.empty())
setObjVarItem(slotIngredients.getNameWithPath()+'.'+OBJVAR_RESOURCE_IMPORTED_DATA, resourceData);
}
}
int ingredientCount=0;
if (slotIngredients.getItem(OBJVAR_RESOURCE_COUNT,ingredientCount))
{
setObjVarItem(slotIngredients.getNameWithPath()+'.'+OBJVAR_RESOURCE_COUNT,
ingredientCount + count);
}
else
{
setObjVarItem(slotIngredients.getNameWithPath() + '.' +
OBJVAR_RESOURCE_COUNT, count);
}
// create the name of the objvar for the resource xp source
char buffer[1024];
sprintf(buffer, "%s.%s.%s", slot.getNameWithPath().c_str(),
OBJVAR_SLOT_SOURCES.c_str(), sourceId.getValueString().c_str());
// see if the xp source already contributed to this slot
int oldCount = 0;
IGNORE_RETURN(getObjVars().getItem(buffer, oldCount));
// set the xp source objvar
setObjVarItem(buffer, count + oldCount);
// tell the client
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->addSlotIngredient(name, resourceId, count);
break;
}
}
} // ManufactureSchematicObject::addSlotResource
//-----------------------------------------------------------------------
/**
* Adds a component to a schematic slot.
*
* @param name the name of the slot
* @param component the component to add
* @param ingredientType the ingredient type that the slot requires
*/
void ManufactureSchematicObject::addSlotComponent(const StringId & name,
const TangibleObject & component, ServerIntangibleObjectTemplate::IngredientType
ingredientType)
{
// Check if the object being added is a factory
const TangibleObject * componentPtr = &component;
const FactoryObject * factory = dynamic_cast<const FactoryObject *>(&component);
if (factory != nullptr)
{
componentPtr = factory->getContainedObject();
if (componentPtr == nullptr)
{
WARNING(true, ("ManufactureSchematicObject::addSlotComponent passed "
"FactoryObject %s with no contained object",
factory->getNetworkId().getValueString().c_str()));
return;
}
}
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot=i.getNestedList();
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name==nameValue))
{
DynamicVariableList::NestedList slotIngredients(slot,OBJVAR_INGREDIENTS);
char buffer[1024];
sprintf(buffer, "%s.%s.%s.%d.", OBJVAR_SLOTS.c_str(), slot.getName().c_str(),
OBJVAR_INGREDIENTS.c_str(), slotIngredients.getCount());
setObjVarItem(buffer + OBJVAR_COMPONENT_NAME, componentPtr->getEncodedObjectName());
setObjVarItem(buffer + OBJVAR_COMPONENT_ID, componentPtr->getNetworkId());
if (ingredientType != ServerIntangibleObjectTemplate::IT_templateGeneric &&
componentPtr->isCrafted())
{
// set the ingredient id
setObjVarItem(buffer + OBJVAR_COMPONENT_CRAFTED_ID, componentPtr->getCraftedId());
}
else
{
// set the ingredient template
setObjVarItem(buffer + OBJVAR_COMPONENT_TEMPLATE_NAME,
Unicode::narrowToWide(componentPtr->getObjectTemplateName()));
}
if (componentPtr->isCrafted())
{
// set the xp type
setObjVarItem(buffer + OBJVAR_COMPONENT_CRAFTED_XP_TYPE, componentPtr->getCreatorXpType());
// add crafter xp data
sprintf(buffer, "%s.%s.%s", slot.getNameWithPath().c_str(),
OBJVAR_SLOT_SOURCES.c_str(), componentPtr->getCreatorId().getValueString().c_str());
setObjVarItem(buffer, componentPtr->getCreatorXp());
}
// transfer component objvars on the component to us
StringId slotName;
slot.getItem(OBJVAR_SLOT_NAME,slotName);
std::string componentCopyName = OBJVAR_COMPONENTS + "." + slotName.getText();
if (!getObjVars().hasItem(componentCopyName))
copyObjVars(componentCopyName, *componentPtr, OBJVAR_COMPONENTS);
// transfer attrib bonuses on the component to us
std::vector<int> attribBonuses;
if (component.getAttribBonuses(attribBonuses))
{
std::vector<float> componentBonuses(attribBonuses.size());
std::copy(attribBonuses.begin(), attribBonuses.end(), componentBonuses.begin());
setObjVarItem(componentCopyName + "." + OBJVAR_ATTRIBUTE_BONUSES, componentBonuses);
}
ManufactureSchematicSynchronizedUi * sync = safe_cast<ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->addSlotIngredient(name, componentPtr->getNetworkId(), 1);
break;
}
}
} // ManufactureSchematicObject::addSlotComponent
//-----------------------------------------------------------------------
/**
* Finds a component in our volume container that matches given component info.
*
* @param info info about the component we want
*
* @return the component
*/
TangibleObject * ManufactureSchematicObject::getComponent(
const Crafting::ComponentIngredient & info) const
{
const VolumeContainer * volumeContainer = ContainerInterface::getVolumeContainer(*this);
if (volumeContainer == nullptr)
{
DEBUG_WARNING(true, ("Manf schematic %s does not have a volume container!",
getNetworkId().getValueString().c_str()));
return nullptr;
}
// check if the component is in a FactoryObject; if it is, return the factory,
// whether it's in our volume container or not
if (info.ingredient != NetworkId::cms_invalid)
{
ServerObject * object = ServerWorld::findObjectByNetworkId(info.ingredient);
if (object != nullptr)
{
Object * container = ContainerInterface::getContainedByObject(*object);
if (container != nullptr && dynamic_cast<FactoryObject*>(container) != nullptr)
return safe_cast<TangibleObject *>(container);
}
}
for (ContainerConstIterator iter = volumeContainer->begin();
iter != volumeContainer->end(); ++iter)
{
const Container::ContainedItem & item = *iter;
TangibleObject * object = safe_cast<TangibleObject *>(item.getObject());
if (object != nullptr)
{
if (info.ingredient != NetworkId::cms_invalid)
{
// need matching crafted id
// if (info.ingredient == object->getCraftedId())
if (info.ingredient == object->getNetworkId())
return object;
}
else
{
// need matching template name
if (info.templateName == object->getObjectTemplateName())
return object;
}
}
}
return nullptr;
} // ManufactureSchematicObject::getComponent
//-----------------------------------------------------------------------
/**
* Sets the ids of the players who are supplying resources for a schematic slot.
*
* @param slotIndex the slot index
* @param sources the ids of the players and the amount of resources they
* supplied
*/
void ManufactureSchematicObject::setSlotSources(int slotIndex,
const std::vector<std::pair<NetworkId, int> > & sources)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
DynamicVariableList::NestedList::const_iterator slotIterator=slots.getItemByPosition(slotIndex);
DynamicVariableList::NestedList slot(slotIterator.getNestedList());
DynamicVariableList::NestedList slotSources(slot,OBJVAR_SLOT_SOURCES);
eraseObjVarListEntries(slotSources.getNameWithPath());
int count = sources.size();
for (int i = 0; i < count; ++i)
{
const std::pair<NetworkId, int> & source = sources[i];
setObjVarItem(slotSources.getNameWithPath()+'.'+source.first.getValueString(),
source.second);
}
} // ManufactureSchematicObject::setSlotSources
//-----------------------------------------------------------------------
/**
* Clears the data for a given slot in the schematic.
*
* @param name the name of the slot to clear
*/
void ManufactureSchematicObject::clearSlot(const StringId & name)
{
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (!draft->getSlot(draftSlot, name))
return;
const DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
const DynamicVariableList::NestedList slot(i.getNestedList());
StringId nameValue;
if (slot.getItem(OBJVAR_SLOT_NAME,nameValue) && (name == nameValue))
{
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_TYPE, Crafting::IT_none);
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_OPTION, -1);
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_COMPLEXITY, draftSlot.complexity);
eraseObjVarListEntries(slot.getNameWithPath()+'.'+OBJVAR_INGREDIENTS);
eraseObjVarListEntries(slot.getNameWithPath()+'.'+OBJVAR_SLOT_SOURCES);
std::string componentData = OBJVAR_COMPONENTS + "." + name.getText();
if (getObjVars().hasItem(componentData))
removeObjVarItem(componentData);
ManufactureSchematicSynchronizedUi * sync = safe_cast<ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->clearSlot(name, draftSlot.complexity);
break;
}
}
} // ManufactureSchematicObject::clearSlot(const StringId &)
//-----------------------------------------------------------------------
/**
* Clears the data for a given slot in the schematic.
*
* @param index index of the slot to clear
*/
void ManufactureSchematicObject::clearSlot(int index)
{
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
DynamicVariableList::NestedList::const_iterator slotIterator(slots.getItemByPosition(index));
DynamicVariableList::NestedList slot(slotIterator.getNestedList());
StringId name;
slot.getItem(OBJVAR_SLOT_NAME, name);
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (!draft->getSlot(draftSlot, name))
return;
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_TYPE, Crafting::IT_none);
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_OPTION, -1);
setObjVarItem(slot.getNameWithPath()+'.'+OBJVAR_SLOT_COMPLEXITY, draftSlot.complexity);
eraseObjVarListEntries(slot.getNameWithPath()+'.'+OBJVAR_INGREDIENTS);
eraseObjVarListEntries(slot.getNameWithPath()+'.'+OBJVAR_SLOT_SOURCES);
std::string componentData = OBJVAR_COMPONENTS + "." + name.getText();
if (getObjVars().hasItem(componentData))
removeObjVarItem(componentData);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->clearSlot(name, draftSlot.complexity);
} // ManufactureSchematicObject::clearSlot(int)
//-----------------------------------------------------------------------
/**
* Clears the sources for all the schematic's slots.
*/
void ManufactureSchematicObject::clearSlotSources()
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin());
i != slots.end(); ++i)
{
DynamicVariableList::NestedList slot = i.getNestedList();
DynamicVariableList::NestedList slotSources(slot,OBJVAR_SLOT_SOURCES);
eraseObjVarListEntries(slotSources.getNameWithPath());
}
// clear any factories being used
if (!m_factories.empty())
{
for (Archive::AutoDeltaVector<CachedNetworkId>::const_iterator iter =
m_factories.begin(); iter != m_factories.end(); ++iter)
{
FactoryObject * factory = safe_cast<FactoryObject *>((*iter).getObject());
if (factory != nullptr)
{
factory->endCraftingSession();
}
}
m_factories.clear();
}
// get rid of all our held ingredients
VolumeContainer * volumeContainer = ContainerInterface::getVolumeContainer(*this);
if (volumeContainer != nullptr && volumeContainer->getNumberOfItems() > 0)
{
// for a stacked ingredient, if there are still more than one items in the stack,
// "consume" 1 item from the stack, and return it to the player's inventory
std::set<ServerObject*> itemsToReturnToInventory;
ServerObject* inventory = nullptr;
for (ContainerIterator iter = volumeContainer->begin();
iter != volumeContainer->end(); ++iter)
{
if ((*iter).getObject() != nullptr)
{
bool destroyItem = true;
TangibleObject* to = safe_cast<ServerObject*>((*iter).getObject())->asTangibleObject();
if (to && (to->getCount() > 1) && (dynamic_cast<FactoryObject *>(to) == nullptr))
{
if (inventory == nullptr)
{
ServerObject * o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*to));
while (o)
{
if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject()))
{
inventory = o->asCreatureObject()->getInventory();
break;
}
o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*o));
}
}
if (inventory)
{
to->setCount(to->getCount() - 1);
itemsToReturnToInventory.insert(to);
destroyItem = false;
}
}
if (destroyItem)
{
safe_cast<ServerObject*>((*iter).getObject())->permanentlyDestroy(
DeleteReasons::Replaced);
}
}
}
if (inventory && !itemsToReturnToInventory.empty())
{
Container::ContainerErrorCode errCode;
for (std::set<ServerObject*>::const_iterator iter = itemsToReturnToInventory.begin(); iter != itemsToReturnToInventory.end(); ++iter)
{
if (!ContainerInterface::transferItemToVolumeContainer(*inventory, **iter, nullptr, errCode))
{
(*iter)->permanentlyDestroy(DeleteReasons::Replaced);
}
}
}
}
eraseObjVarListEntries(OBJVAR_STORED_RESOURCES);
} // ManufactureSchematicObject::clearSlotSources()
//-----------------------------------------------------------------------
/**
* Clears the sources for a given schematic slot.
*
* @param index the slot index
*/
void ManufactureSchematicObject::clearSlotSources(int index)
{
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
DynamicVariableList::NestedList slot(slots,index);
DynamicVariableList::NestedList slotSources(slot,OBJVAR_SLOT_SOURCES);
eraseObjVarListEntries(slotSources.getNameWithPath());
} // ManufactureSchematicObject::clearSlotSources(int)
//-----------------------------------------------------------------------
/**
* Removes an optional slot from the schematic.
*
* @param name the name of the slot to remove
*/
void ManufactureSchematicObject::removeSlot(const StringId & name)
{
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (!draft->getSlot(draftSlot, name))
return;
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot(i.getNestedList());
StringId varName;
if (slot.getItem(OBJVAR_SLOT_NAME,varName) && (name == varName))
{
std::string slotName(OBJVAR_SLOTS + "." + slot.getName());
computeComplexity();
removeObjVarItem(slotName);
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->removeSlot(name);
break;
}
}
} // ManufactureSchematicObject::removeSlot
//-----------------------------------------------------------------------
/**
* Sets an approximate experiment mod for an assembled schematic so the player
* will have some idea of the risk experimenting with an item.
*
* @param mod approximent mod that will be applied during experimentation
*/
void ManufactureSchematicObject::setExperimentMod(float mod)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setExperimentMod(mod);
} // ManufactureSchematicObject::setExperimentMod
//-----------------------------------------------------------------------
/**
* Returns a list of the experimental attribute names.
*
* @return the name list
*/
const std::vector<StringId> & ManufactureSchematicObject::getExperimentAttributeNames() const
{
static const std::vector<StringId> emptyList;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
return sync->getAttributeNames();
else
return emptyList;
} // ManufactureSchematicObject::getExperimentAttributeNames
//-----------------------------------------------------------------------
/**
* Returns the value of an experimental attribute.
*
* @param name name of the attribute
*
* @return value of the attribute
*/
float ManufactureSchematicObject::getExperimentAttribute(const StringId & name) const
{
float result = 0;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
result = sync->getAttribute(name);
return result;
} // ManufactureSchematicObject::getExperimentAttribute
//-----------------------------------------------------------------------
/**
* Sets the value of an experimental attribute.
*
* @param name name of the attribute
* @param value value of the attribute
*/
void ManufactureSchematicObject::setExperimentAttribute(const StringId & name,
float value)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setAttribute(name, value);
} // ManufactureSchematicObject::setExperimentAttribute
//-----------------------------------------------------------------------
/**
* Returns the min value of an experimental attribute.
*
* @param name name of the attribute
*
* @return min value of the attribute
*/
float ManufactureSchematicObject::getMinExperimentAttribute(const StringId & name) const
{
float result = 0;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
result = sync->getMinAttribute(name);
return result;
} // ManufactureSchematicObject::getMinExperimentAttribute
//-----------------------------------------------------------------------
/**
* Sets the min value of an experimental attribute.
*
* @param name name of the attribute
* @param value min value of the attribute
*/
void ManufactureSchematicObject::setMinExperimentAttribute(const StringId & name,
float value)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setMinAttribute(name, value);
} // ManufactureSchematicObject::setMinExperimentAttribute
//-----------------------------------------------------------------------
/**
* Returns the max value of an experimental attribute.
*
* @param name name of the attribute
*
* @return max value of the attribute
*/
float ManufactureSchematicObject::getMaxExperimentAttribute(const StringId & name) const
{
float result = 0;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
result = sync->getMaxAttribute(name);
return result;
} // ManufactureSchematicObject::getMaxExperimentAttribute
//-----------------------------------------------------------------------
/**
* Sets the max value of an experimental attribute.
*
* @param name name of the attribute
* @param value max value of the attribute
*/
void ManufactureSchematicObject::setMaxExperimentAttribute(const StringId & name,
float value)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setMaxAttribute(name, value);
} // ManufactureSchematicObject::setMaxExperimentAttribute
//-----------------------------------------------------------------------
/**
* Returns the resource max value of an experimental attribute.
*
* @param name name of the attribute
*
* @return resource max value of the attribute
*/
float ManufactureSchematicObject::getResourceMaxExperimentAttribute(const StringId & name) const
{
float result = 0;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
result = sync->getResourceMaxAttribute(name);
return result;
} // ManufactureSchematicObject::getResourceMaxExperimentAttribute
//-----------------------------------------------------------------------
/**
* Sets the resource max value of an experimental attribute.
*
* @param name name of the attribute
* @param value resource max value of the attribute
*/
void ManufactureSchematicObject::setResourceMaxExperimentAttribute(const StringId & name,
float value)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setResourceMaxAttribute(name, value);
} // ManufactureSchematicObject::setResourceMaxExperimentAttribute
//-----------------------------------------------------------------------
/**
* Returns all the values associated with an experimental attribute.
*
* @param name name of the attribute
* @param value filled in with the attribute value
* @param minValue filled in with the min attribute value
* @param maxValue filled in with the max attribute value
* @param resourceMaxValue filled in with the resource max attribute value
*/
void ManufactureSchematicObject::getAllExperimentAttributeValues(const StringId & name,
float & value, float & minValue, float & maxValue, float & resourceMaxValue) const
{
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->getAllAttributeValues(name, value, minValue, maxValue, resourceMaxValue);
else
value = minValue = maxValue = resourceMaxValue = 0;
} // ManufactureSchematicObject::getAllExperimentAttributeValues
//-----------------------------------------------------------------------
/**
* Sets the limits of an experimental attribute.
*
* @param name name of the attribute
* @param minValue min attribute value
* @param maxValue max attribute value
* @param resourceMaxValue resource max attribute value
*/
void ManufactureSchematicObject::setExperimentAttributeLimits(const StringId & name,
float minValue, float maxValue, float resourceMaxValue)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setAttributeLimits(name, minValue, maxValue, resourceMaxValue);
} // ManufactureSchematicObject::setExperimentAttributeLimits
//-----------------------------------------------------------------------
/**
* Returns the list of available appearances a crafter may choose from.
*
* @return the appearance list
*/
const std::vector<std::string> & ManufactureSchematicObject::getAppearances() const
{
static const std::vector<std::string> emptyList;
const ManufactureSchematicSynchronizedUi * sync = safe_cast<
const ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
return sync->getAppearances();
return emptyList;
} // ManufactureSchematicObject::getAppearances
//-----------------------------------------------------------------------
/**
* Adds an appearance to the list of available appearances.
*
* @param appearance the appearance filename
*/
void ManufactureSchematicObject::addAppearance(const std::string & appearance)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<
ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->addAppearance(appearance);
} // ManufactureSchematicObject::addAppearance
//-----------------------------------------------------------------------
/**
* Returns the custom appearance objects made by this schematic will use.
*
* @return the appearance filename
*/
const std::string ManufactureSchematicObject::getCustomAppearance() const
{
static const std::string emptyString;
std::string result;
if (!getObjVars().getItem("customAppearance",result))
return emptyString;
return result;
} // ManufactureSchematicObject::getCustomAppearance
//-----------------------------------------------------------------------
/**
* Sets the custom appearance objects made by this schematic will use.
*
* @param appearance the appearance filename
*/
void ManufactureSchematicObject::setCustomAppearance(const std::string & appearance)
{
m_customAppearance = appearance;
setObjVarItem("customAppearance", appearance);
} // ManufactureSchematicObject::setCustomAppearance(const std::string &)
//-----------------------------------------------------------------------
/**
* Sets the custom appearance objects made by this schematic will use, based on
* the appearance list available to a crafter.
*
* @param index index into the appearance list to use
*/
void ManufactureSchematicObject::setCustomAppearance(int index)
{
const std::vector<std::string> & appearances = getAppearances();
if (index < 0 || index >= static_cast<int>(appearances.size()))
return;
setCustomAppearance(appearances[index]);
} // ManufactureSchematicObject::setCustomAppearance(int)
//-----------------------------------------------------------------------
/**
* Returns customization data for the schematic.
*
* @param name the name of the customization data
* @param data structure to be filled in with the customization data
*
* @return true on success, false if the customization name wasn't found
*
bool ManufactureSchematicObject::getCustomization(const std::string & name,
Crafting::CustomInfo & data) const
{
const DynamicVariableList * objvars = getObjVars();
NOT_NULL(objvars);
const DynamicVariableList * customs = safe_cast<const DynamicVariableList *>(objvars->getItemByName("cust"));
if (customs == nullptr)
return false;
const DynamicVariableList * custom = safe_cast<const DynamicVariableList *>(customs->getItemByName(name));
if (custom == nullptr)
return false;
data.name = name;
data.index = safe_cast<const DynamicVariableInt *>(custom->getItemByName("index"))->getValue();
data.minIndex = safe_cast<const DynamicVariableInt *>(custom->getItemByName("mindex"))->getValue();
data.maxIndex = safe_cast<const DynamicVariableInt *>(custom->getItemByName("maxdex"))->getValue();
return true;
} // ManufactureSchematicObject::getCustomization(const std::string &)
*/
//-----------------------------------------------------------------------
/**
* Returns customization data for the schematic.
*
* @param index index of the customization data
* @param data structure to be filled in with the customization data
*
* @return true on success, false if the index was out of range
*
bool ManufactureSchematicObject::getCustomization(int index,
Crafting::CustomInfo & data) const
{
const DynamicVariableList * objvars = getObjVars();
NOT_NULL(objvars);
const DynamicVariableList * customs = safe_cast<const DynamicVariableList *>(objvars->getItemByName("cust"));
if (customs == nullptr)
return false;
int count = customs->getCount();
if (index < 0 || index >= count)
return false;
const DynamicVariableList * custom = safe_cast<const DynamicVariableList *>(customs->getItemByPosition(index));
NOT_NULL(custom);
data.name = custom->getName();
data.index = safe_cast<const DynamicVariableInt *>(custom->getItemByName("index"))->getValue();
data.minIndex = safe_cast<const DynamicVariableInt *>(custom->getItemByName("mindex"))->getValue();
data.maxIndex = safe_cast<const DynamicVariableInt *>(custom->getItemByName("maxdex"))->getValue();
return true;
} // ManufactureSchematicObject::getCustomization(int)
*/
//-----------------------------------------------------------------------
/**
* Returns the number of customization options available for the schematic.
*
* @return the customization count
*
int ManufactureSchematicObject::getCustomizationsCount() const
{
const DynamicVariableList * objvars = getObjVars();
NOT_NULL(objvars);
const DynamicVariableList * customs = safe_cast<const DynamicVariableList *>(objvars->getItemByName("cust"));
if (customs == nullptr)
return 0;
return customs->getCount();
} // ManufactureSchematicObject::getCustomizationsCount
*/
//-----------------------------------------------------------------------
/**
* Sets or adds customization data to the schematic.
*
* @param name the customization data name
* @param value value of the customization data
* @param minValue minimum possible value of the customization data
* @param maxValue maximum possible value of the customization data
*/
void ManufactureSchematicObject::setCustomization(const std::string & name,
int value, int minValue, int maxValue)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync)
sync->setCustomization(name, value, minValue, maxValue);
} // ManufactureSchematicObject::setCustomization(const std::string &, int, int, int)
//-----------------------------------------------------------------------
/**
* Sets the value of customization data of the schematic.
*
* @param index index of the customization data
* @param value value of the customization data
* @param prototype prototype object to change the customization of
*
* @return true if the value was set, false if not
*/
bool ManufactureSchematicObject::setCustomization(int index, int value,
ServerObject & prototype)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync == nullptr)
return false;
const std::string & customName = sync->getCustomizationName(index);
if (customName.empty())
return false;
return setCustomization(customName, value, prototype);
} // ManufactureSchematicObject::setCustomization(int, int, ServerObject *)
//-----------------------------------------------------------------------
/**
* Sets the value of customization data of the schematic.
*
* @param name the customization data name
* @param value value of the customization data
* @param prototype prototype object to change the customization of
*
* @return true if the value was set, false if not
*/
bool ManufactureSchematicObject::setCustomization(const std::string & name,
int value, ServerObject & prototype)
{
ManufactureSchematicSynchronizedUi * sync = safe_cast<ManufactureSchematicSynchronizedUi *>(getSynchronizedUi ());
if (sync == nullptr)
return false;
// make sure the value is within range
if (value < sync->getMinCustomization(name))
return false;
if (value > sync->getMaxCustomization(name))
return false;
sync->setCustomization(name, value);
// update the prototype
prototype.customize(name, value);
// save the customization string
CustomizationDataProperty * const cdProperty = safe_cast<CustomizationDataProperty*>(prototype.getProperty(CustomizationDataProperty::getClassPropertyId()));
if (cdProperty != nullptr)
{
CustomizationData *const customizationData = cdProperty->fetchCustomizationData();
if (customizationData != nullptr)
{
m_appearanceData = customizationData->writeLocalDataToString();
setObjVarItem("customization_data", m_appearanceData.get());
}
}
return true;
} // ManufactureSchematicObject::setCustomization(const std::string &, int, ServerObject *)
//-----------------------------------------------------------------------
/**
* Computes the complexity of the schematic based on the ingredients being used.
*/
void ManufactureSchematicObject::computeComplexity()
{
static const StringId COMPLEXITY_ID("crafting", "complexity");
const DraftSchematicObject * draft = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
NOT_NULL(draft);
float complexity = draft->getComplexity();
DynamicVariableList::NestedList slots(getObjVars(),"ingr");
{
for (DynamicVariableList::NestedList::const_iterator i(slots.begin()); i!=slots.end(); ++i)
{
DynamicVariableList::NestedList slot(i.getNestedList());
int varType=0;
float varComplex=0;
if (slot.getItem("type",varType) && slot.getItem("complex",varComplex) && static_cast<enum Crafting::IngredientType>(varType) != Crafting::IT_none)
complexity += varComplex;
}
}
// set the current value of the "complexity" attribute
setAttribute(COMPLEXITY_ID, complexity);
setComplexity(complexity);
} // ManufactureSchematicObject::computeComplexity
//-----------------------------------------------------------------------
/**
* Tells us that a FactoryObject has references that are being used as components
* during crafting. This will allow us to remove/clean up the components.
*
* @param factory the factory object
*/
void ManufactureSchematicObject::addCraftingFactory(const FactoryObject & factory)
{
if (isAuthoritative())
{
CachedNetworkId id(factory);
if (m_factories.find(id) < 0)
m_factories.push_back(id);
else
{
WARNING(true, ("ManufactureSchematicObject::addCraftingFactory %s tried "
"to add factory %s that was already added",
getNetworkId().getValueString().c_str(),
factory.getNetworkId().getValueString().c_str()));
}
}
else
{
WARNING(true, ("ManufactureSchematicObject::addCraftingFactory called on "
"non-authoritative object %s", getNetworkId().getValueString().c_str()));
}
} // ManufactureSchematicObject::addCraftingFactory
//-----------------------------------------------------------------------
/**
* Tells us that a FactoryObject is being removed from a crafting session.
* In general this should only be called if the factory is being removed by
* some unexpected event, not as part of the crafting process.
*
* @param factory the factory object
*/
void ManufactureSchematicObject::removeCraftingFactory(const FactoryObject & factory)
{
if (isAuthoritative())
{
CachedNetworkId id(factory);
int index = m_factories.find(id);
if (index >= 0)
m_factories.erase(index);
}
else
{
WARNING(true, ("ManufactureSchematicObject::removeCraftingFactory called on "
"non-authoritative object %s", getNetworkId().getValueString().c_str()));
}
} // ManufactureSchematicObject::removeCraftingFactory
//-----------------------------------------------------------------------
/**
* Add a component ingredient to the schematic's volume container.
*
* @param component the ingredient to add
*
* @return true on success, false on fail
*/
bool ManufactureSchematicObject::addIngredient(ServerObject & component)
{
Container::ContainerErrorCode tmp = Container::CEC_Success;
return ContainerInterface::transferItemToVolumeContainer(*this, component, nullptr, tmp);
} // ManufactureSchematicObject::addIngredient
//-----------------------------------------------------------------------
/**
* Add a resource ingredient to the schematic's objvars.
*
* @param resourceId the resource type id
* @param count the amount of resource
* @param sourceId who mined the resource
*
* @return true on success, false on fail
*/
bool ManufactureSchematicObject::addIngredient(const NetworkId & resourceId, int count, const NetworkId & sourceId)
{
std::string objvarName(OBJVAR_STORED_RESOURCES + "." + resourceId.getValueString() + "." + sourceId.getValueString());
// get how much the source has already contributed the resource
int currentCount = 0;
getObjVars().getItem(objvarName, currentCount);
// update the source's count
currentCount += count;
setObjVarItem(objvarName, currentCount);
return true;
} // ManufactureSchematicObject::addIngredient
//-----------------------------------------------------------------------
/**
* Remove a component ingredient from the schematic's volume container.
*
* @param component the component to remove
* @param destination the container to put the component into
*
* @return true on success, false on fail
*/
bool ManufactureSchematicObject::removeIngredient(ServerObject & component,
ServerObject & destination)
{
// if the component is a factory, treat is differently
FactoryObject * factory = dynamic_cast<FactoryObject *>(&component);
if (factory != nullptr)
{
// if the factory is contained by us, move it to the destination,
// making sure that its count is 1; if it is not contained by us,
// just increase its count
if (ContainerInterface::getContainedByObject(*factory) == this)
{
// since components are removed one at a time from us, the factory
// should only contain one item when it is removed
if (factory->getCount() > 1)
factory->removeCraftingReferences(factory->getCount() - 1);
Container::ContainerErrorCode errCode;
return ContainerInterface::transferItemToVolumeContainer(destination,
*factory, nullptr, errCode);
}
else
{
// for the moment, just increment the factory count, even if the factory
// isn't in the destination container
factory->addCraftingReferences(1);
return true;
}
}
else
{
// make sure the component is in our container
Object * container = ContainerInterface::getContainedByObject(component);
if (container == nullptr || container->getNetworkId() != getNetworkId())
{
FactoryObject * factory = dynamic_cast<FactoryObject *>(container);
if (factory != nullptr)
return removeIngredient(*factory, destination);
return false;
}
Container::ContainerErrorCode errCode;
return ContainerInterface::transferItemToVolumeContainer(destination, component,
nullptr, errCode);
}
} // ManufactureSchematicObject::removeIngredient
//-----------------------------------------------------------------------
/**
* Remove a resource ingredient from the schematic's objvars.
*
* @param resourceId the resource type id
* @param count the amount of resource
* @param destination the resource container to put the resource in
*
* @return true on success, false on fail
*/
bool ManufactureSchematicObject::removeIngredient(const NetworkId & resourceId, int count, ResourceContainerObject & destination)
{
// make sure the destination holds the resource type
if (destination.getResourceTypeId() != resourceId &&
destination.getResourceTypeId() != NetworkId::cms_invalid)
{
return false;
}
// make sure the destination has enough room
if (destination.getMaxQuantity() - destination.getQuantity() < count)
return false;
// find the resource from our objvars
std::vector<std::string> removedObjvars;
const std::string objvarName(OBJVAR_STORED_RESOURCES + "." + resourceId.getValueString());
{
DynamicVariableList::NestedList sources(getObjVars(), objvarName);
for (DynamicVariableList::NestedList::const_iterator i = sources.begin(); count > 0 && i != sources.end(); ++i)
{
int sourceCount = 0;
i.getValue(sourceCount);
if (sourceCount > count)
{
IGNORE_RETURN(destination.addResource(resourceId, count, NetworkId(i.getName())));
setObjVarItem(i.getNameWithPath(), sourceCount - count);
count = 0;
}
else
{
IGNORE_RETURN(destination.addResource(resourceId, sourceCount, NetworkId(i.getName())));
removedObjvars.push_back(i.getNameWithPath());
count -= sourceCount;
}
}
}
{
// remove sources that have all their contribution removed
for (std::vector<std::string>::const_iterator i = removedObjvars.begin();
i != removedObjvars.end(); ++i)
{
removeObjVarItem(*i);
}
}
if (count > 0)
return false;
return true;
} // ManufactureSchematicObject::removeIngredient
//----------------------------------------------------------------------
bool ManufactureSchematicObject::hasIngredient (const NetworkId & componentId) const
{
const ServerObject * const obj = safe_cast<const ServerObject *>(NetworkIdManager::getObjectById (componentId));
if (obj && ContainerInterface::getContainedByObject (*obj) == this)
return true;
return false;
}
//-----------------------------------------------------------------------
/**
* Destroys all the ingredients in the volume container and resets our slots.
*/
void ManufactureSchematicObject::destroyAllIngredients()
{
if (isAuthoritative())
{
// clear any factories being used
if (!m_factories.empty())
{
for (Archive::AutoDeltaVector<CachedNetworkId>::const_iterator iter =
m_factories.begin(); iter != m_factories.end(); ++iter)
{
FactoryObject * factory = safe_cast<FactoryObject *>((*iter).getObject());
if (factory != nullptr)
{
factory->endCraftingSession();
}
}
m_factories.clear();
}
// empty our volume container
VolumeContainer * const container = ContainerInterface::getVolumeContainer(*this);
if (container != nullptr)
{
// for a stacked ingredient, if there are still more than one items in the stack,
// "consume" 1 item from the stack, and return it to the player's inventory
std::set<ServerObject*> itemsToReturnToInventory;
ServerObject* inventory = nullptr;
ContainerIterator iter;
for (iter = container->begin(); iter != container->end(); ++iter)
{
CachedNetworkId & itemId = *iter;
if (itemId.getObject() != nullptr)
{
bool destroyItem = true;
TangibleObject* to = safe_cast<ServerObject*>(itemId.getObject())->asTangibleObject();
if (to && (to->getCount() > 1) && (dynamic_cast<FactoryObject *>(to) == nullptr))
{
if (inventory == nullptr)
{
ServerObject * o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*to));
while (o)
{
if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject()))
{
inventory = o->asCreatureObject()->getInventory();
break;
}
o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*o));
}
}
if (inventory)
{
to->setCount(to->getCount() - 1);
itemsToReturnToInventory.insert(to);
destroyItem = false;
}
}
if (destroyItem)
{
safe_cast<ServerObject*>(itemId.getObject())->permanentlyDestroy(DeleteReasons::Consumed);
}
}
else
{
WARNING_STRICT_FATAL(true, ("Manf schematic %s has ingredient id %s with no object", getNetworkId().getValueString().c_str(),itemId.getValueString().c_str()));
}
}
if (inventory && !itemsToReturnToInventory.empty())
{
Container::ContainerErrorCode errCode;
for (std::set<ServerObject*>::const_iterator iter = itemsToReturnToInventory.begin(); iter != itemsToReturnToInventory.end(); ++iter)
{
if (!ContainerInterface::transferItemToVolumeContainer(*inventory, **iter, nullptr, errCode))
{
(*iter)->permanentlyDestroy(DeleteReasons::Consumed);
}
}
}
}
// dump the resource objvars
eraseObjVarListEntries(OBJVAR_STORED_RESOURCES);
// reset our slots
const DynamicVariableList::NestedList slots(getObjVars(),"ingr");
int count = slots.getCount();
for (int i = 0; i < count; ++i)
{
clearSlot(i);
}
}
else
{
WARNING(true, ("ManufactureSchematicObject::destroyAllIngredients called on "
"non-authoritative object %s", getNetworkId().getValueString().c_str()));
}
} // ManufactureSchematicObject::destroyAllIngredients
//-----------------------------------------------------------------------
/**
* Creates an item with this schematic. It is assumed that the ingredients
* needed to make it are available. Note that this function is duplicated in
* FactoryObject::manufactureObject; if this function changes, that one should
* too.
*
* @param creatorId the id of the player making the object
* @param container the container to create the object in
* @param containerSlotId the slot in the container to put the object in
* @param prototype flag that we are creating a prototype, and shouldn't
* check for the correct ingredients
*
* @return the manufactured object
*/
ServerObject * ManufactureSchematicObject::manufactureObject(const NetworkId & creatorId, ServerObject & container, const SlotId & containerSlotId, bool prototype)
{
if (getCount() <= 0)
return nullptr;
const DraftSchematicObject * draftSchematic = DraftSchematicObject::getSchematic(m_draftSchematic.get());
if (draftSchematic == nullptr)
{
return nullptr;
}
if (draftSchematic->getCraftedObjectTemplate() == nullptr)
{
DEBUG_WARNING(true, ("Draft schematic %s does not have a craftable object template!\n", draftSchematic->getTemplateName()));
return nullptr;
}
// create the item
TangibleObject * const object =
dynamic_cast<TangibleObject *>(ServerWorld::createNewObject(*draftSchematic->getCraftedObjectTemplate(), container, containerSlotId, false));
if (object == nullptr)
{
DEBUG_WARNING(true, ("Player %s failed to create object for template "
"%s.\n", creatorId.getValueString().c_str(),
draftSchematic->getCraftedObjectTemplate()->getName()));
return nullptr;
}
if (!getAssignedObjectName().empty())
object->setObjectName(getAssignedObjectName());
// set the object appearance
const std::string & appearance = getCustomAppearance();
if (!appearance.empty())
object->setCustomAppearance(appearance);
// set the customization data for the object
if (!m_appearanceData.get().empty())
object->setAppearanceData(m_appearanceData.get());
// mark the item as having been created by us
object->setCraftedId(getNetworkId());
object->setCraftedType(draftSchematic->getCategory());
object->setCreatorId(creatorId);
object->setOwnerId(creatorId);
// set the creator xp type from the schematic template
const ServerObjectTemplate * schematicTemplate = safe_cast<const
ServerObjectTemplate *>(draftSchematic->getObjectTemplate());
if (schematicTemplate != nullptr && schematicTemplate->getXpPointsCount() > 0)
{
ServerObjectTemplate::Xp xpData;
schematicTemplate->getXpPoints(xpData, 0);
object->setCreatorXpType(xpData.type);
}
if (!prototype)
{
if (!setObjectComponents(object, true))
{
object->permanentlyDestroy(DeleteReasons::BadContainerTransfer);
return nullptr;
}
// allow the schematic scripts to modify the object
// prototype objects are modified at the end of the crafting session
m_isMakingObject = true;
ScriptParams params;
params.addParam(creatorId);
params.addParam(object->getNetworkId());
params.addParam(*this);
params.addParam(false);
params.addParam(true);
int result = getScriptObject()->trigAllScripts(Scripting::TRIG_MANUFACTURE_OBJECT, params);
m_isMakingObject = false;
if (result == SCRIPT_OVERRIDE)
{
object->permanentlyDestroy(DeleteReasons::Script);
return nullptr;
}
incrementCount(-1);
clearSlotSources();
if (getCount() == 0)
{
// delete myself
permanentlyDestroy(DeleteReasons::Consumed);
}
}
else
{
// flag the object as a prototype
setObjVarItem(OBJVAR_CRAFTING_IS_PROTOTYPE, true);
}
return object;
} // ManufactureSchematicObject::manufactureObject(const NetworkId &, ServerObject &, const SlotId &, bool)
//-----------------------------------------------------------------------
/**
* Creates an item with this schematic. This is for use in creating a finished
* object without ingredients.
*
* @position where to create the object
*
* @return the manufactured object
*/
ServerObject * ManufactureSchematicObject::manufactureObject(const Vector & position)
{
const DraftSchematicObject * draftSchematic = DraftSchematicObject::getSchematic(
m_draftSchematic.get());
if (draftSchematic == nullptr)
{
return nullptr;
}
if (draftSchematic->getCraftedObjectTemplate() == nullptr)
{
DEBUG_WARNING(true, ("Draft schematic %s does not have a craftable "
"object template!\n", draftSchematic->getTemplateName()));
return nullptr;
}
Transform tr;
tr.setPosition_p(position);
// create the item
TangibleObject * object = dynamic_cast<TangibleObject *>(ServerWorld::createNewObject(
*draftSchematic->getCraftedObjectTemplate(), tr, 0,
false));
if (object == nullptr)
{
DEBUG_WARNING(true, ("Failed to create object for template "
"%s.\n", draftSchematic->getCraftedObjectTemplate()->getName()));
return nullptr;
}
if (!getAssignedObjectName().empty())
object->setObjectName(getAssignedObjectName());
// set the object appearance
const std::string & appearance = getCustomAppearance();
if (!appearance.empty())
object->setCustomAppearance(appearance);
// set the customization data for the object
if (!m_appearanceData.get().empty())
object->setAppearanceData(m_appearanceData.get());
// mark the item as having been created by us
object->setCraftedId(getNetworkId());
object->setCraftedType(draftSchematic->getCategory());
object->setCreatorId(NetworkId::cms_invalid);
// flag the object as a prototype
setObjVarItem(OBJVAR_CRAFTING_IS_PROTOTYPE, true);
return object;
} // ManufactureSchematicObject::manufactureObject(const Vector &)
//-----------------------------------------------------------------------
/**
* Transfers the components in the schematic's container to an object it has
* created.
*
* @param object the object that was created
* @param checkResources flag to verify there are enough resources to craft
* the object
*
* @return true on success, false if there was an error
*/
bool ManufactureSchematicObject::setObjectComponents(TangibleObject * object, bool checkResources)
{
static const uint32 INVALID_CRC = Crc::normalizeAndCalculate("invalid");
int i;
const DraftSchematicObject * draftSchematic = DraftSchematicObject::getSchematic(m_draftSchematic.get());
NOT_NULL(draftSchematic);
// get the visible components string; if missing, flag this as the 1st
// item made
bool firstItem = false;
std::vector<int> visibleComponents;
if (!getObjVars().getItem("visComp",visibleComponents))
firstItem = true;
// check the objects in the schematic volume container to make sure they match
VolumeContainer * volumeContainer = ContainerInterface::getVolumeContainer(*this);
if (volumeContainer == nullptr)
{
DEBUG_WARNING(true, ("Manf schematic %s does not have a volume container!",
getNetworkId().getValueString().c_str()));
return false;
}
// for a stacked ingredient, if there are still more than one items in the stack,
// "consume" 1 item from the stack, and return it to the player's inventory
std::set<ServerObject*> itemsToReturnToInventory;
ServerObject* inventory = nullptr;
DynamicVariableList::NestedList slots(getObjVars(),OBJVAR_SLOTS);
// check the objects in the schematic volume container to make sure they match
// the ingredients needed
Crafting::IngredientSlot slotData;
ContainerIterator iter;
Crafting::Ingredients::const_iterator ingredientIter;
std::vector<std::pair<NetworkId, int> > sources;
int slotCount = getSlotsCount();
for (i = 0; i < slotCount; ++i)
{
if (!getSlot(i, slotData, false))
break;
if (slotData.ingredients.empty())
break;
DynamicVariableList::NestedList slot(slots,i);
DynamicVariableList::NestedList slotIngredients(slot,OBJVAR_INGREDIENTS);
sources.clear();
for (ingredientIter = slotData.ingredients.begin();
ingredientIter != slotData.ingredients.end(); ++ingredientIter)
{
const Crafting::SimpleIngredient * ingredient = (*ingredientIter).get();
NOT_NULL(ingredient);
int ingredientCount = ingredient->count;
if (ingredientCount <= 0)
{
WARNING_STRICT_FATAL(true, ("Manufacture schematic %s has "
"invalid ingredient %s count %d",
getNetworkId().getValueString().c_str(),
ingredient->ingredient.getValueString().c_str(), ingredientCount));
break;
}
if (slotData.ingredientType == Crafting::IT_item ||
slotData.ingredientType == Crafting::IT_template ||
slotData.ingredientType == Crafting::IT_templateGeneric ||
slotData.ingredientType == Crafting::IT_schematic ||
slotData.ingredientType == Crafting::IT_schematicGeneric)
{
const Crafting::ComponentIngredient * component = dynamic_cast<
const Crafting::ComponentIngredient *>(ingredient);
NOT_NULL(component);
// find an ingredient in our volume container that matches the
// component
for (iter = volumeContainer->begin();
iter != volumeContainer->end() && ingredientCount > 0;
++iter)
{
Container::ContainedItem & item = *iter;
TangibleObject * const testComponent = dynamic_cast<TangibleObject *>(item.getObject());
if (testComponent == nullptr)
continue;
if (itemsToReturnToInventory.count(testComponent) > 0)
continue;
FactoryObject * crate = nullptr;
bool found = false;
bool foundCrate = false;
if (component->ingredient != NetworkId::cms_invalid)
{
if (component->ingredient == testComponent->getCraftedId())
{
found = true;
// see if the test component is a crate
crate = dynamic_cast<FactoryObject *>(testComponent);
if (crate != nullptr && crate->getCount() == ingredientCount)
{
foundCrate = true;
}
}
}
else
{
if (testComponent->getObjectTemplateName() == component->templateName)
found = true;
}
if (found)
{
if (foundCrate)
ingredientCount = 0;
else
--ingredientCount;
if (testComponent->isCrafted())
{
// set the xp type for the component
int ingredientIndex = ingredientIter -
slotData.ingredients.begin();
DynamicVariableList::NestedList ingredientVars(slotIngredients,ingredientIndex);
{
setObjVarItem(ingredientVars.getContextName()+
OBJVAR_COMPONENT_CRAFTED_XP_TYPE,
testComponent->getCreatorXpType());
}
// add xp info to the sources list
sources.push_back(std::make_pair(testComponent->getCreatorId(),
testComponent->getCreatorXp()));
}
// if the component is visible when attached to the object,
// get it's id from the component table and add it to the
// visible components list
if (firstItem)
{
ServerDraftSchematicObjectTemplate::IngredientSlot draftSlot;
if (draftSchematic->getSlot(draftSlot, slotData.draftSlotIndex))
{
if (!draftSlot.appearance.empty())
{
// check with the table for this type of component
const std::string & componentTableName = ConfigServerGame::getCraftingComponentTableFile(draftSlot.appearance);
if (!componentTableName.empty())
{
DataTable * componentTable = DataTableManager::getTable(componentTableName, true);
if (componentTable != nullptr)
{
uint32 value = INVALID_CRC;
if (draftSlot.appearance == "component")
{
// use the component name directly
int row;
if (foundCrate)
row = componentTable->searchColumnString(0, crate->getContainedSharedTemplateName());
else
row = componentTable->searchColumnString(0, testComponent->getSharedTemplateName());
if (row >= 0)
{
value = componentTable->getIntValue(0, row);
}
}
else
{
// check if the component is compatable with the crafted object, and use a default component if not
int row = componentTable->searchColumnString(0, object->getObjectTemplateName());
int column;
if (foundCrate)
column = componentTable->findColumnNumber(crate->getItemCustomAppearance());
else
column = componentTable->findColumnNumber(testComponent->getCustomAppearance());
if (row >= 0 && column >= 0)
{
value = componentTable->getIntValue(column, row);
}
if (row >= 0 && value == INVALID_CRC)
{
column = componentTable->findColumnNumber("default");
if (column >= 0)
value = componentTable->getIntValue(column, row);
}
}
if (value != INVALID_CRC)
{
visibleComponents.push_back(value);
}
}
}
}
}
}
bool destroyItem = true;
if ((testComponent->getCount() > 1) && (dynamic_cast<FactoryObject *>(testComponent) == nullptr))
{
if (inventory == nullptr)
{
ServerObject * o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*testComponent));
while (o)
{
if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject()))
{
inventory = o->asCreatureObject()->getInventory();
break;
}
o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*o));
}
}
if (inventory)
{
testComponent->setCount(testComponent->getCount() - 1);
itemsToReturnToInventory.insert(testComponent);
destroyItem = false;
}
}
if (destroyItem)
{
testComponent->permanentlyDestroy(DeleteReasons::Consumed);
}
}
}
}
else if (checkResources && (
slotData.ingredientType == Crafting::IT_resourceType ||
slotData.ingredientType == Crafting::IT_resourceClass))
{
//@todo: check resources against the OBJVAR_STORED_RESOURCES data
}
else if (checkResources)
break;
}
if (ingredientIter != slotData.ingredients.end())
break;
if (!sources.empty())
{
// set up the sources so they'll get experience
setSlotSources(i, sources);
}
}
if (inventory && !itemsToReturnToInventory.empty())
{
Container::ContainerErrorCode errCode;
for (std::set<ServerObject*>::const_iterator iter = itemsToReturnToInventory.begin(); iter != itemsToReturnToInventory.end(); ++iter)
{
if (!ContainerInterface::transferItemToVolumeContainer(*inventory, **iter, nullptr, errCode))
{
(*iter)->permanentlyDestroy(DeleteReasons::Consumed);
}
}
itemsToReturnToInventory.clear();
}
if (i != slotCount)
return false;
// see if we have anything left in our volume container, and delete them
if (volumeContainer->getNumberOfItems() > 0)
{
WARNING(true, ("ManufactureSchematicObject::setObjectComponents schematic "
"%s(%s) has extra ingredients! Deleting them.",
getNetworkId().getValueString().c_str(),
draftSchematic->getCraftedObjectTemplate()->getName()));
for (iter = volumeContainer->begin(); iter != volumeContainer->end(); ++iter)
{
if ((*iter).getObject() != nullptr)
{
ServerObject * temp = safe_cast<ServerObject*>((*iter).getObject());
bool destroyItem = true;
TangibleObject* to = temp->asTangibleObject();
if (to && (to->getCount() > 1) && (dynamic_cast<FactoryObject *>(to) == nullptr))
{
if (inventory == nullptr)
{
ServerObject * o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*to));
while (o)
{
if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject()))
{
inventory = o->asCreatureObject()->getInventory();
break;
}
o = safe_cast<ServerObject*>(ContainerInterface::getContainedByObject(*o));
}
}
if (inventory)
{
to->setCount(to->getCount() - 1);
itemsToReturnToInventory.insert(to);
destroyItem = false;
}
}
if (destroyItem)
{
WARNING(true, ("ManufactureSchematicObject::setObjectComponents "
"deleting extra ingredient %s(%s).",
(*iter).getValueString().c_str(),
temp->getObjectTemplateName()));
temp->permanentlyDestroy(DeleteReasons::Replaced);
}
}
else
{
WARNING(true, ("ManufactureSchematicObject::setObjectComponents "
"extra ingredient %s has no object.",
(*iter).getValueString().c_str()));
}
}
}
eraseObjVarListEntries(OBJVAR_STORED_RESOURCES);
// tell the object what components are visible
object->setVisibleComponents(visibleComponents);
if (firstItem)
{
// add the visible component list to our objvars
setObjVarItem("visComp", visibleComponents);
}
if (inventory && !itemsToReturnToInventory.empty())
{
Container::ContainerErrorCode errCode;
for (std::set<ServerObject*>::const_iterator iter = itemsToReturnToInventory.begin(); iter != itemsToReturnToInventory.end(); ++iter)
{
if (!ContainerInterface::transferItemToVolumeContainer(*inventory, **iter, nullptr, errCode))
{
(*iter)->permanentlyDestroy(DeleteReasons::Replaced);
}
}
}
return true;
} // ManufactureSchematicObject::setObjectComponents
// ----------------------------------------------------------------------
/**
* Sends info about the ingredients needed by the schematic to a player.
*
* @param player the player to send the data to
*/
void ManufactureSchematicObject::requestSlots(ServerObject & player) const
{
Crafting::IngredientSlot slot;
MessageQueueDraftSlots::Slot slotInfo;
MessageQueueDraftSlots::Option optionInfo;
Crafting::Ingredients::const_iterator ingredientIter;
MessageQueueDraftSlotsQueryResponse * const message =
new MessageQueueDraftSlotsQueryResponse(std::make_pair(m_draftSchematic.get(),
m_draftSchematicSharedTemplate.get()));
int slotCount = getSlotsCount();
for (int i = 0; i < slotCount; ++i)
{
if (!getSlot(i, slot, false))
continue;
if (slot.ingredientType != Crafting::IT_invalid &&
slot.ingredientType != Crafting::IT_none)
{
slotInfo.name = slot.name;
slotInfo.optional = false;
optionInfo.type = slot.ingredientType;
// get the number of ingredients needed
optionInfo.amountNeeded = 0;
for (ingredientIter = slot.ingredients.begin();
ingredientIter != slot.ingredients.end(); ++ingredientIter)
{
optionInfo.amountNeeded += (*ingredientIter)->count;
}
// get the ingredient name
const NetworkId & ingredientId = slot.ingredients.front()->ingredient;
switch (slot.ingredientType)
{
case Crafting::IT_item:
case Crafting::IT_template:
case Crafting::IT_templateGeneric:
case Crafting::IT_schematic:
case Crafting::IT_schematicGeneric:
{
const TangibleObject * const ingredient = dynamic_cast<const TangibleObject *>(NetworkIdManager::getObjectById(ingredientId));
if (ingredient != nullptr)
{
optionInfo.ingredient = ingredient->getEncodedObjectName();
// since names can be duplicated, concatinate the id of
// the manf schematic to the name
if (ingredient->isCrafted())
{
optionInfo.ingredient += Unicode::narrowToWide(TangibleObject::generateSerialNumber(ingredient->getCraftedId()));
}
else
{
optionInfo.ingredient += Unicode::narrowToWide(TangibleObject::generateSerialNumber(ingredient->getNetworkId()));
}
}
else
optionInfo.ingredient = Unicode::narrowToWide("unknown");
}
break;
case Crafting::IT_resourceType:
case Crafting::IT_resourceClass:
{
ResourceTypeObject const * const ingredient = ServerUniverse::getInstance().getResourceTypeById(ingredientId);
if (ingredient != nullptr)
optionInfo.ingredient = Unicode::narrowToWide(ingredient->getResourceName());
else
optionInfo.ingredient = Unicode::narrowToWide("unknown");
}
break;
default:
break;
}
slotInfo.options.push_back(optionInfo);
message->addSlot(slotInfo);
slotInfo.options.clear();
}
}
} // ManufactureSchematicObject::requestSlots
//----------------------------------------------------------------------
void ManufactureSchematicObject::getIngredientInfo (IngredientInfoVector & iiv) const
{
Crafting::IngredientSlot slot;
Unicode::String ingredientName;
typedef std::map<Unicode::String, std::pair<bool, int> > IngredientMap;
IngredientMap imap;
// compute the total ingredients needed for the schematic
int slotCount = getSlotsCount();
for (int i = 0; i < slotCount; ++i)
{
if (!getSlot(i, slot, false))
continue;
if (slot.ingredientType == Crafting::IT_invalid ||
slot.ingredientType == Crafting::IT_none)
continue;
// get the number of ingredients needed
int amountNeeded = 0;
for (Crafting::Ingredients::const_iterator ingredientIter = slot.ingredients.begin();
ingredientIter != slot.ingredients.end(); ++ingredientIter)
{
amountNeeded += (*ingredientIter)->count;
}
ingredientName.clear ();
if (amountNeeded <= 0)
continue;
bool isComponent = false;
// get the ingredient name
const Crafting::SimpleIngredient * const info = slot.ingredients.front().get();
NOT_NULL(info);
const NetworkId & ingredientId = info->ingredient;
switch (slot.ingredientType)
{
case Crafting::IT_item:
case Crafting::IT_template:
case Crafting::IT_schematic:
{
const Crafting::ComponentIngredient * const component = safe_cast<const Crafting::ComponentIngredient *>(info);
NOT_NULL (component);
ingredientName = component->componentName;
// since names can be duplicated, concatenate the id of
// the manf schematic to the name
if (component->ingredient != NetworkId::cms_invalid)
{
//-- nullptr-separate the string if needed
if (!ingredientName.empty () && ingredientName [0] == '@')
ingredientName.push_back ('\0');
ingredientName.push_back (' ');
ingredientName += Unicode::narrowToWide (TangibleObject::generateSerialNumber (component->ingredient));
}
isComponent = true;
}
break;
case Crafting::IT_templateGeneric:
case Crafting::IT_schematicGeneric:
{
const Crafting::ComponentIngredient * const component = safe_cast<const Crafting::ComponentIngredient *>(info);
NOT_NULL (component);
const ServerObjectTemplate * const serverTemplate = dynamic_cast<const ServerObjectTemplate *>(ObjectTemplateList::fetch (component->templateName));
if (serverTemplate)
{
const std::string & sharedTemplateName = serverTemplate->getSharedTemplate ();
const SharedObjectTemplate * const sharedTemplate = dynamic_cast<const SharedObjectTemplate *>(ObjectTemplateList::fetch (sharedTemplateName));
if (sharedTemplate)
{
ingredientName.push_back ('@');
ingredientName += Unicode::narrowToWide (sharedTemplate->getObjectName ().getCanonicalRepresentation ());
sharedTemplate->releaseReference ();
}
else
WARNING (true, ("ManufactureSchematicObject templateGeneric shared template [%s] not found for server template [%s]", sharedTemplateName.c_str (), component->templateName.c_str ()));
serverTemplate->releaseReference ();
}
else
WARNING (true, ("ManufactureSchematicObject templateGeneric server template [%s] not found", component->templateName.c_str ()));
isComponent = true;
}
break;
case Crafting::IT_resourceType:
case Crafting::IT_resourceClass:
{
ResourceTypeObject const * const ingredient = ServerUniverse::getInstance().getResourceTypeById(ingredientId);
if (ingredient != nullptr)
ingredientName = Unicode::narrowToWide(ingredient->getResourceName ());
}
break;
default:
break;
}
if (!ingredientName.empty ())
{
const IngredientMap::iterator result = imap.find (ingredientName);
if (result == imap.end())
imap.insert (std::make_pair(ingredientName, std::make_pair (isComponent, amountNeeded)));
else
{
int & totalAmountNeeded = (*result).second.second;
totalAmountNeeded += amountNeeded;
}
}
}
iiv.reserve (iiv.size () + imap.size ());
{
for (IngredientMap::const_iterator iter = imap.begin(); iter != imap.end(); ++iter)
{
const Unicode::String & name = (*iter).first;
const bool isComponent = (*iter).second.first;
const int amount = (*iter).second.second;
iiv.push_back (IngredientInfo (name, std::make_pair (isComponent, amount)));
}
}
}
//----------------------------------------------------------------------
/**
* Returns the attribute bonuses this object grants when equipped.
*
* @param attribBonuses list to be filled in with the bonuses
*/
void ManufactureSchematicObject::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()));
}
}
} // ManufactureSchematicObject::getAttribBonuses
//----------------------------------------------------------------------
/**
* Returns the attribute bonus of this object for a given attribute.
*
* @param attribute the attribute
*
* @return the bonus for the attribute
*/
int ManufactureSchematicObject::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;
} // ManufactureSchematicObject::getAttribBonus
//----------------------------------------------------------------------
/**
* Sets the attribute bonus this object applies when equipped.
*
* @param attribute the attribute
* @param bonus the bonus
*/
void ManufactureSchematicObject::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);
} // ManufactureSchematicObject::setAttribBonus
// ----------------------------------------------------------------------
const std::map<StringId, float> & ManufactureSchematicObject::getAttributes() const
{
return m_attributes.getMap();
}
// ----------------------------------------------------------------------
/**
* Returns the volume the schematic takes up in a player's datapad. The volume
* is defined by the draft schematic template.
*/
int ManufactureSchematicObject::getVolume() const
{
const DraftSchematicObject * const schematic = DraftSchematicObject::getSchematic(getDraftSchematic());
if (schematic != nullptr)
return schematic->getObjectTemplate()->asServerObjectTemplate()->getVolume();
return IntangibleObject::getVolume();
} // ManufactureSchematicObject::getVolume
// ----------------------------------------------------------------------
void ManufactureSchematicObject::getAttributes (AttributeVector &data) const
{
char valueBuffer[32];
const size_t valueBuffer_size = sizeof (valueBuffer);
IntangibleObject::getAttributes(data);
static IngredientInfoVector iiv;
iiv.clear ();
getIngredientInfo (iiv);
data.reserve (data.size () + iiv.size () + 3);
snprintf (valueBuffer, valueBuffer_size, "%d", getVolume ());
data.push_back (std::make_pair (SharedObjectAttributes::data_volume, Unicode::narrowToWide (valueBuffer)));
for (IngredientInfoVector::const_iterator it = iiv.begin (); it != iiv.end (); ++it)
{
const Unicode::String & name = (*it).first;
const int amount = (*it).second.second;
if (name.empty ())
continue;
snprintf(valueBuffer, valueBuffer_size, "%i", amount);
if (name [0] == '@')
data.push_back (std::make_pair (SharedObjectAttributes::cat_manf_schem_ing_resource + "." + Unicode::wideToNarrow (name), Unicode::narrowToWide (valueBuffer)));
else
data.push_back (std::make_pair (SharedObjectAttributes::cat_manf_schem_ing_resource + ".\"" + Unicode::wideToNarrow (name), Unicode::narrowToWide (valueBuffer)));
}
snprintf(valueBuffer, valueBuffer_size, "%i", getCount());
data.push_back(std::make_pair(SharedObjectAttributes::manf_limit, Unicode::narrowToWide(valueBuffer)));
//-- show manufactured object attribs
data.push_back(std::make_pair(SharedObjectAttributes::manf_attribs, Unicode::narrowToWide("\\#pcontrast2 ----------\\#.")));
retrieveStoredObjectAttributes (data);
}
// ----------------------------------------------------------------------
void ManufactureSchematicObject::recalculateData()
{
if (isAuthoritative())
{
// copy the OBJVAR_ATTRIBUTES objvars to the m_attributes member
if (getObjVars().hasItem(OBJVAR_ATTRIBUTES))
{
const DynamicVariableList::NestedList attributes(getObjVars(), OBJVAR_ATTRIBUTES);
for (DynamicVariableList::NestedList::const_iterator i = attributes.begin();
i != attributes.end(); ++i)
{
float value;
i.getValue(value);
m_attributes.set(StringId(i.getName()), value);
}
removeObjVarItem(OBJVAR_ATTRIBUTES);
}
// bug fix - set the schematic's complexity from the "crafting:complexity" attrib
Archive::AutoDeltaMap<StringId, float>::const_iterator found = m_attributes.find(ATTRIBUTES_COMPLEXITY);
if (found != m_attributes.end())
{
float complexity = (*found).second;
if (complexity != getComplexity())
setComplexity(complexity);
}
const DraftSchematicObject * const schematic = DraftSchematicObject::getSchematic(m_draftSchematic.get());
if (schematic)
{
m_draftSchematicSharedTemplate = schematic->getSharedTemplate()->getCrcName().getCrc();
}
else
{
const DraftSchematicObject * const missingSchematic = DraftSchematicObject::getSchematic(MISSING_SCHEMATIC_SUBSTITUTE);
if (missingSchematic != nullptr)
m_draftSchematicSharedTemplate = missingSchematic->getSharedTemplate()->getCrcName().getCrc();
else
WARNING_STRICT_FATAL(true, ("Cannot find draft schematic %s! This must always exist!", MISSING_SCHEMATIC_SUBSTITUTE.c_str()));
}
m_customAppearance = getCustomAppearance();
std::string appearanceString;
if (getObjVars().getItem("customization_data",appearanceString))
m_appearanceData = appearanceString;
}
// resource types for imported resources used in the manufacturing schematic
// needs to be registered on this game server; this must be done on each game
// server as it receives the manufacturing schematic
DynamicVariableList::NestedList const slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i = slots.begin(); i != slots.end(); ++i)
{
DynamicVariableList::NestedList const slot(i.getNestedList());
if (slot.hasItem(OBJVAR_INGR_RESOURCE_IMPORTED_DATA) && slot.hasItem(OBJVAR_INGR_RESOURCE_ID))
{
NetworkId id;
if (slot.getItem(OBJVAR_INGR_RESOURCE_ID, id) && !ServerUniverse::getInstance().getImportedResourceTypeById(id))
{
std::string resourceData;
if (slot.getItem(OBJVAR_INGR_RESOURCE_IMPORTED_DATA, resourceData))
{
NetworkId const resourceTypeId = ResourceTypeObject::addImportedResourceType(resourceData);
if (isAuthoritative() && resourceTypeId.isValid())
{
if (resourceTypeId.getValue() <= NetworkId::cms_maxNetworkIdWithoutClusterId)
removeObjVarItem(slot.getNameWithPath() + '.' + OBJVAR_INGR_RESOURCE_IMPORTED_DATA);
if (resourceTypeId != id)
IGNORE_RETURN(setObjVarItem(slot.getNameWithPath() + '.' + OBJVAR_INGR_RESOURCE_ID, resourceTypeId));
}
}
}
}
}
}
// ----------------------------------------------------------------------
void ManufactureSchematicObject::getByteStreamFromAutoVariable(const std::string & name, Archive::ByteStream & target) const
{
if (name == "msoCtsPackUnpack")
{
// creator Id/Name
CreatureObject const * containingPlayer = nullptr;
ServerObject const * parent = safe_cast<ServerObject const*>(ContainerInterface::getContainedByObject(*this));
while (parent)
{
containingPlayer = parent->asCreatureObject();
if (containingPlayer && PlayerCreatureController::getPlayerObject(containingPlayer))
break;
containingPlayer = nullptr;
parent = safe_cast<ServerObject const*>(ContainerInterface::getContainedByObject(*parent));
}
bool const creatorIsOwner = (containingPlayer && (containingPlayer->getNetworkId() == m_creatorId.get()));
Archive::AutoDeltaVariable<bool>(creatorIsOwner).pack(target);
// items per container
m_itemsPerContainer.pack(target);
// manufacture time
m_manufactureTime.pack(target);
// attributes
m_attributes.pack(target);
// resource ingredients
Archive::AutoDeltaMap<std::string, std::pair<NetworkId, std::string> > resourceIngredients;
DynamicVariableList::NestedList const slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i = slots.begin(); i != slots.end(); ++i)
{
DynamicVariableList::NestedList const slot(i.getNestedList());
if (slot.hasItem(OBJVAR_INGR_RESOURCE_ID))
{
NetworkId id;
if (slot.getItem(OBJVAR_INGR_RESOURCE_ID, id))
{
ResourceTypeObject const * const rto = ServerUniverse::getInstance().getResourceTypeById(id);
if (rto)
resourceIngredients.set(slot.getNameWithPath(), std::make_pair(((id.getValue() <= NetworkId::cms_maxNetworkIdWithoutClusterId) ? NetworkId(id.getValueWithClusterId(GameServer::getInstance().getClusterId(), static_cast<NetworkId::NetworkIdType>(1), NetworkId::cms_maxNetworkIdWithoutClusterId)) : id), rto->getResourceTypeDataForExport()));
}
}
}
resourceIngredients.pack(target);
// the manufacturing schematic "original" NetworkId; see the comment for getOriginalId() for more information
NetworkId const originalId = getOriginalId();
Archive::AutoDeltaVariable<NetworkId>(originalId).pack(target);
}
else
{
IntangibleObject::getByteStreamFromAutoVariable(name, target);
}
}
// ----------------------------------------------------------------------
void ManufactureSchematicObject::setAutoVariableFromByteStream(const std::string & name, const Archive::ByteStream & source)
{
Archive::ReadIterator ri(source);
if (name == "msoCtsPackUnpack")
{
// creator Id/Name
Archive::AutoDeltaVariable<bool> creatorIsOwner;
creatorIsOwner.unpackDelta(ri);
CreatureObject const * containingPlayer = nullptr;
if (creatorIsOwner.get())
{
ServerObject const * parent = safe_cast<ServerObject const*>(ContainerInterface::getContainedByObject(*this));
while (parent)
{
containingPlayer = parent->asCreatureObject();
if (containingPlayer && PlayerCreatureController::getPlayerObject(containingPlayer))
break;
containingPlayer = nullptr;
parent = safe_cast<ServerObject const*>(ContainerInterface::getContainedByObject(*parent));
}
}
if (creatorIsOwner.get() && containingPlayer)
{
m_creatorId = containingPlayer->getNetworkId();
m_creatorName = containingPlayer->getObjectName();
replaceStoredObjectAttribute(Unicode::narrowToWide(SharedObjectAttributes::crafter), Unicode::toLower(containingPlayer->getAssignedObjectFirstName()));
}
else
{
// if the manufacturing schematic's creator is transferring the manufacturing schematic,
// then on the destination galaxy, preserve the manufacturing schematic's creator
// information by setting the destination manufacturing schematic's creator to the
// destination character; otherwise, set the destination manufacturing schematic's creator
// to some non-zero oid that cannot possibly be a player on the target galaxy (like 1), to
// make it look like the manufacturing schematic's creator is no longer on the galaxy,
// like the manufacturing schematic's creator has been deleted and removed from the DB
m_creatorId = NetworkId(static_cast<NetworkId::NetworkIdType>(10000000)); // 10,000,000 is max gold oid
m_creatorName = Unicode::String();
replaceStoredObjectAttribute(Unicode::narrowToWide(SharedObjectAttributes::crafter), Unicode::String());
}
// items per container
m_itemsPerContainer.unpackDelta(ri);
// manufacture time
m_manufactureTime.unpackDelta(ri);
// attributes
typedef Archive::AutoDeltaMap<StringId, float>::Command AttributeCommands;
std::vector<AttributeCommands> attributes;
Archive::AutoDeltaMap<StringId, float>::unpack(ri, attributes);
m_attributes.clear();
for (std::vector<AttributeCommands>::const_iterator attributeIter = attributes.begin(); attributeIter != attributes.end(); ++attributeIter)
m_attributes.set(attributeIter->key, attributeIter->value);
// resource ingredients
typedef Archive::AutoDeltaMap<std::string, std::pair<NetworkId, std::string> >::Command ResourceIngredientCommands;
std::vector<ResourceIngredientCommands> resourceIngredients;
Archive::AutoDeltaMap<std::string, std::pair<NetworkId, std::string> >::unpack(ri, resourceIngredients);
for (std::vector<ResourceIngredientCommands>::const_iterator resourceIngredientIter = resourceIngredients.begin(); resourceIngredientIter != resourceIngredients.end(); ++resourceIngredientIter)
{
IGNORE_RETURN(setObjVarItem(resourceIngredientIter->key + '.' + OBJVAR_INGR_RESOURCE_ID, resourceIngredientIter->value.first));
IGNORE_RETURN(setObjVarItem(resourceIngredientIter->key + '.' + OBJVAR_INGR_RESOURCE_IMPORTED_DATA, resourceIngredientIter->value.second));
}
// the manufacturing schematic "original" NetworkId; see the comment for getOriginalId() for more information
Archive::AutoDeltaVariable<NetworkId> originalId;
originalId.unpackDelta(ri);
removeObjVarItem(OBJVAR_ORIGINAL_OBJECT_ID);
IGNORE_RETURN(setObjVarItem(OBJVAR_ORIGINAL_OBJECT_ID, originalId.get()));
recalculateData();
// need to tell all proxy game servers, if any, to create the imported resource
// types for any ingredient, if it hasn't already been created on that game server
ProxyList const &proxyList = getExposedProxyList();
if (!proxyList.empty())
{
bool calledServerMessageForwardingBegin = false;
DynamicVariableList::NestedList const slots(getObjVars(),OBJVAR_SLOTS);
for (DynamicVariableList::NestedList::const_iterator i = slots.begin(); i != slots.end(); ++i)
{
DynamicVariableList::NestedList const slot(i.getNestedList());
if (slot.hasItem(OBJVAR_INGR_RESOURCE_IMPORTED_DATA) && slot.hasItem(OBJVAR_INGR_RESOURCE_ID))
{
NetworkId id;
std::string resourceData;
if (slot.getItem(OBJVAR_INGR_RESOURCE_ID, id) && (id.getValue() > NetworkId::cms_maxNetworkIdWithoutClusterId) && slot.getItem(OBJVAR_INGR_RESOURCE_IMPORTED_DATA, resourceData))
{
if (!calledServerMessageForwardingBegin)
{
ServerMessageForwarding::begin(std::vector<uint32>(proxyList.begin(), proxyList.end()));
calledServerMessageForwardingBegin = true;
}
GenericValueTypeMessage<std::pair<NetworkId, std::string> > const addImportedResourceTypeMsg("AddImportedResourceType", std::make_pair(id, resourceData));
ServerMessageForwarding::send(addImportedResourceTypeMsg);
}
}
}
if (calledServerMessageForwardingBegin)
ServerMessageForwarding::end();
}
}
else
{
IntangibleObject::setAutoVariableFromByteStream(name, source);
}
addObjectToConcludeList();
}
// ----------------------------------------------------------------------
// crates that get generated from a manufacturing schematic use the
// NetworkId of the manufacturing schematic to generate the crate's
// (and the items pulled from the crate) serial number; however, when
// a manufacturing schematic is CTS(ed), it gets a different NetworkId
// on the destination cluster, thus causing it to create crate with a
// different serial number; in order to preserve the serial number
// of crates generated from a manufacturing schematic regardless of
// how many times the manufacturing schematic gets CTS(ed), for
// manufacturing schematic that is created because of CTS, we need
// to preserve the NetworkId of the original schematic and use that
// NetworkId when generating the crate from the schematic
NetworkId ManufactureSchematicObject::getOriginalId() const
{
DynamicVariableList const & objvar = getObjVars();
if (objvar.hasItem(OBJVAR_ORIGINAL_OBJECT_ID) && (objvar.getType(OBJVAR_ORIGINAL_OBJECT_ID) == DynamicVariable::NETWORK_ID))
{
NetworkId originalId;
IGNORE_RETURN(objvar.getItem(OBJVAR_ORIGINAL_OBJECT_ID, originalId));
return originalId;
}
return getNetworkId();
}
// ======================================================================