//======================================================================== // // 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(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(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(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(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 & 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(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(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(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(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(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(&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 attribBonuses; if (component.getAttribBonuses(attribBonuses)) { std::vector componentBonuses(attribBonuses.size()); std::copy(attribBonuses.begin(), attribBonuses.end(), componentBonuses.begin()); setObjVarItem(componentCopyName + "." + OBJVAR_ATTRIBUTE_BONUSES, componentBonuses); } ManufactureSchematicSynchronizedUi * sync = safe_cast(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(container) != nullptr) return safe_cast(container); } } for (ContainerConstIterator iter = volumeContainer->begin(); iter != volumeContainer->end(); ++iter) { const Container::ContainedItem & item = *iter; TangibleObject * object = safe_cast(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 > & 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 & 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(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::const_iterator iter = m_factories.begin(); iter != m_factories.end(); ++iter) { FactoryObject * factory = safe_cast((*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 itemsToReturnToInventory; ServerObject* inventory = nullptr; for (ContainerIterator iter = volumeContainer->begin(); iter != volumeContainer->end(); ++iter) { if ((*iter).getObject() != nullptr) { bool destroyItem = true; TangibleObject* to = safe_cast((*iter).getObject())->asTangibleObject(); if (to && (to->getCount() > 1) && (dynamic_cast(to) == nullptr)) { if (inventory == nullptr) { ServerObject * o = safe_cast(ContainerInterface::getContainedByObject(*to)); while (o) { if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject())) { inventory = o->asCreatureObject()->getInventory(); break; } o = safe_cast(ContainerInterface::getContainedByObject(*o)); } } if (inventory) { to->setCount(to->getCount() - 1); itemsToReturnToInventory.insert(to); destroyItem = false; } } if (destroyItem) { safe_cast((*iter).getObject())->permanentlyDestroy( DeleteReasons::Replaced); } } } if (inventory && !itemsToReturnToInventory.empty()) { Container::ContainerErrorCode errCode; for (std::set::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 & ManufactureSchematicObject::getExperimentAttributeNames() const { static const std::vector 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 & ManufactureSchematicObject::getAppearances() const { static const std::vector 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 & appearances = getAppearances(); if (index < 0 || index >= static_cast(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(objvars->getItemByName("cust")); if (customs == nullptr) return false; const DynamicVariableList * custom = safe_cast(customs->getItemByName(name)); if (custom == nullptr) return false; data.name = name; data.index = safe_cast(custom->getItemByName("index"))->getValue(); data.minIndex = safe_cast(custom->getItemByName("mindex"))->getValue(); data.maxIndex = safe_cast(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(objvars->getItemByName("cust")); if (customs == nullptr) return false; int count = customs->getCount(); if (index < 0 || index >= count) return false; const DynamicVariableList * custom = safe_cast(customs->getItemByPosition(index)); NOT_NULL(custom); data.name = custom->getName(); data.index = safe_cast(custom->getItemByName("index"))->getValue(); data.minIndex = safe_cast(custom->getItemByName("mindex"))->getValue(); data.maxIndex = safe_cast(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(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(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(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(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(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(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(&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(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 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::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(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::const_iterator iter = m_factories.begin(); iter != m_factories.end(); ++iter) { FactoryObject * factory = safe_cast((*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 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(itemId.getObject())->asTangibleObject(); if (to && (to->getCount() > 1) && (dynamic_cast(to) == nullptr)) { if (inventory == nullptr) { ServerObject * o = safe_cast(ContainerInterface::getContainedByObject(*to)); while (o) { if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject())) { inventory = o->asCreatureObject()->getInventory(); break; } o = safe_cast(ContainerInterface::getContainedByObject(*o)); } } if (inventory) { to->setCount(to->getCount() - 1); itemsToReturnToInventory.insert(to); destroyItem = false; } } if (destroyItem) { safe_cast(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::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(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(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(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 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 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 > 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(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(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(testComponent) == nullptr)) { if (inventory == nullptr) { ServerObject * o = safe_cast(ContainerInterface::getContainedByObject(*testComponent)); while (o) { if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject())) { inventory = o->asCreatureObject()->getInventory(); break; } o = safe_cast(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::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((*iter).getObject()); bool destroyItem = true; TangibleObject* to = temp->asTangibleObject(); if (to && (to->getCount() > 1) && (dynamic_cast(to) == nullptr)) { if (inventory == nullptr) { ServerObject * o = safe_cast(ContainerInterface::getContainedByObject(*to)); while (o) { if (o->asCreatureObject() && PlayerCreatureController::getPlayerObject(o->asCreatureObject())) { inventory = o->asCreatureObject()->getInventory(); break; } o = safe_cast(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::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(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 > 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(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(info); NOT_NULL (component); const ServerObjectTemplate * const serverTemplate = dynamic_cast(ObjectTemplateList::fetch (component->templateName)); if (serverTemplate) { const std::string & sharedTemplateName = serverTemplate->getSharedTemplate (); const SharedObjectTemplate * const sharedTemplate = dynamic_cast(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 > & 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 & 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::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(ContainerInterface::getContainedByObject(*this)); while (parent) { containingPlayer = parent->asCreatureObject(); if (containingPlayer && PlayerCreatureController::getPlayerObject(containingPlayer)) break; containingPlayer = nullptr; parent = safe_cast(ContainerInterface::getContainedByObject(*parent)); } bool const creatorIsOwner = (containingPlayer && (containingPlayer->getNetworkId() == m_creatorId.get())); Archive::AutoDeltaVariable(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 > 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(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(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 creatorIsOwner; creatorIsOwner.unpackDelta(ri); CreatureObject const * containingPlayer = nullptr; if (creatorIsOwner.get()) { ServerObject const * parent = safe_cast(ContainerInterface::getContainedByObject(*this)); while (parent) { containingPlayer = parent->asCreatureObject(); if (containingPlayer && PlayerCreatureController::getPlayerObject(containingPlayer)) break; containingPlayer = nullptr; parent = safe_cast(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(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::Command AttributeCommands; std::vector attributes; Archive::AutoDeltaMap::unpack(ri, attributes); m_attributes.clear(); for (std::vector::const_iterator attributeIter = attributes.begin(); attributeIter != attributes.end(); ++attributeIter) m_attributes.set(attributeIter->key, attributeIter->value); // resource ingredients typedef Archive::AutoDeltaMap >::Command ResourceIngredientCommands; std::vector resourceIngredients; Archive::AutoDeltaMap >::unpack(ri, resourceIngredients); for (std::vector::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 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(proxyList.begin(), proxyList.end())); calledServerMessageForwardingBegin = true; } GenericValueTypeMessage > 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(); } // ======================================================================