// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "action_phrase_faber.h" #include "../client_sheets/sbrick_sheet.h" #include "interface_manager.h" #include "../sheet_manager.h" #include "inventory_manager.h" #include "action_handler.h" #include "../client_cfg.h" #include "ctrl_base_button.h" #include "group_container.h" #include "../string_manager_client.h" #include "../net_manager.h" #include "sbrick_manager.h" #include "sphrase_manager.h" #include "group_editbox.h" #include "dbview_bar.h" #include "skill_manager.h" using namespace std; using namespace NLMISC; // *************************************************************************** const std::string FaberPlanDB= "UI:PHRASE:FABER:FABER_PLAN:SHEET"; const std::string MPFaberDB= "UI:PHRASE:FABER:MP_BUILD"; const std::string MPSelectionDB= "UI:PHRASE:FABER:MP_SELECT"; const std::string MPQuantityDb= "UI:PHRASE:FABER:MP_QUANTITY"; const std::string MPQuantitySelectDb= "UI:PHRASE:FABER:STACK_SELECT"; const std::string ItemResultSheetDB= "UI:PHRASE:FABER:RESULT_ITEM:SHEET"; const std::string ItemResultQuantityDB= "UI:PHRASE:FABER:RESULT_ITEM:QUANTITY"; const std::string ItemResultSheetLevel= "UI:PHRASE:FABER:RESULT_ITEM:QUALITY"; const std::string ItemResultSheetColor= "UI:PHRASE:FABER:RESULT_ITEM:USER_COLOR"; const std::string ItemResultSheetClassType= "UI:PHRASE:FABER:RESULT_ITEM:RM_CLASS_TYPE"; const std::string ItemResultSheetStatType= "UI:PHRASE:FABER:RESULT_ITEM:RM_FABER_STAT_TYPE"; const std::string FaberPhraseWindow= "ui:interface:phrase_faber_execution"; const std::string FaberPhraseItemReqLine= FaberPhraseWindow + ":header_opened:item_reqs:item_req_%d"; const std::string FaberPhraseList= "list"; const std::string FaberPhraseText= "text"; const std::string FaberPhraseIcon= "icon"; const std::string FaberPhraseValidButton= FaberPhraseWindow + ":header_opened:ok_cancel:ok"; const std::string FaberPhraseFpCtrl= FaberPhraseWindow + ":header_opened:faber_plan"; const std::string FaberPhraseFpSuccessText= FaberPhraseWindow + ":header_opened:success_text"; const std::string FaberPhraseMpListModal= "ui:interface:phrase_faber_mp_selection"; const std::string FaberPhraseMpQuantityModal= "ui:interface:phrase_faber_mp_quantity"; const std::string FaberPhraseItemResultGroup= FaberPhraseWindow + ":header_opened:item_result"; #define MAX_MP_SELECTION_ENTRIES 256 // *************************************************************************** CActionPhraseFaber::CActionPhraseFaber() { _InventoryMirror.resize(INVENTORIES::NUM_INVENTORY * MAX_PLAYER_INV_ENTRIES); _InventoryObsSetup= false; _ExecuteFromItemPlanBrick= NULL; } // *************************************************************************** void CActionPhraseFaber::fillDBWithMP(const std::string &sheetBase, const CItem &item) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); pIM->getDbProp(sheetBase + ":SHEET")->setValue32(item.Sheet.asInt()); pIM->getDbProp(sheetBase + ":QUALITY")->setValue32(item.Quality); pIM->getDbProp(sheetBase + ":QUANTITY")->setValue32(item.Quantity); pIM->getDbProp(sheetBase + ":USER_COLOR")->setValue32(item.UserColor); pIM->getDbProp(sheetBase + ":WEIGHT")->setValue32(item.Weight); } // *************************************************************************** void CActionPhraseFaber::launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick) { // **** Get the ItemSheet for faber plan. NULL => no op. if(!rootBrick) return; // Copy Execution launch _ExecuteFromMemoryLine= memoryLine; _ExecuteFromMemoryIndex= memoryIndex; // no item plan setuped for now _ExecuteFromItemPlanBrick= NULL; // get the family of item plan (for selection) from the rootBrick. It is stored in the Property0. _FaberPlanBrickFamilies.clear(); if(rootBrick->Properties.size()>0) { string prop= NLMISC::toUpper(rootBrick->Properties[0].Text); vector strList; splitString(prop, " ", strList); // The prop Id should be 'FPLAN:' if(strList.size()>=2 && strList[0]=="FPLAN:") { for(uint i=1;i Can't select plan to craft", rootBrick->Id.toString().c_str() ); return; } // **** Hide all widgets, MP Ctrls, and reset DB, until the Plan is not selected CInterfaceManager *pIM= CInterfaceManager::getInstance(); // Hide the valid button CCtrlBaseButton *validButton= dynamic_cast(pIM->getElementFromId(FaberPhraseValidButton)); if(validButton) validButton->setFrozen(true); // reset DB, hide the Mps uint itemReqLine; for(itemReqLine=0;itemReqLinegetDbProp(toString("%s:%d:%d:SHEET", MPFaberDB.c_str(), itemReqLine, mpSlot), false); if(node) node->setValue32(0); } // Hide item requirements groups per default CInterfaceGroup *itemReqLineGroup= dynamic_cast(pIM->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) )); if(itemReqLineGroup) itemReqLineGroup->setActive(false); } // Reset the selected plan CCDBNodeLeaf *node= pIM->getDbProp(FaberPlanDB, false); if(node) node->setValue32(0); // Reset the result item node= pIM->getDbProp(ItemResultSheetDB, false); if(node) node->setValue32(0); // Hide the ItemResult group CInterfaceGroup *groupMp= dynamic_cast(pIM->getElementFromId(FaberPhraseItemResultGroup)); if(groupMp) groupMp->setActive(false); // **** Open the window! CGroupContainer *window= dynamic_cast(pIM->getElementFromId(FaberPhraseWindow)); if(window) { window->setActive(true); // Setup the Title with a default text ucstring title= CI18N::get("uiPhraseFaberExecuteNoPlan"); window->setUCTitle (title); } // **** setup DB observer! // ensure remove (if setuped before), then add CCDBNodeBranch *branch; branch= pIM->getDbBranch("LOCAL:INVENTORY:BAG"); if(branch) branch->removeBranchObserver(&_DBInventoryObs); if(branch) branch->addBranchObserver(&_DBInventoryObs); // and for all pack animals uint i; for(i=0;igetDbBranch(toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i)); if(branch) branch->removeBranchObserver(&_DBInventoryObs); if(branch) branch->addBranchObserver(&_DBInventoryObs); } // Add observers on animal status, cause inventory may become unavailabe during the process for(i=0;igetDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS",i), false); if(node) { ICDBNode::CTextId textId; node->addObserver(&_DBAnimalObs, textId); } } // Observe skill status change to update success rate CSkillManager *pSM= CSkillManager::getInstance(); pSM->appendSkillChangeCallback(&_SkillObserver); } // *************************************************************************** void CActionPhraseFaber::onCloseFaberCastWindow() { CInterfaceManager *pIM= CInterfaceManager::getInstance(); CSkillManager *pSM= CSkillManager::getInstance(); // No more need to listen inventory change CCDBNodeBranch *branch; branch= pIM->getDbBranch("LOCAL:INVENTORY:BAG"); if(branch) branch->removeBranchObserver(&_DBInventoryObs); // and for all pack animals for(uint i=0;igetDbBranch(toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i)); if(branch) branch->removeBranchObserver(&_DBInventoryObs); } // remove observers on animal status, cause inventory may become unavailabe during the process for(uint i=0;igetDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS",i), false); if(node) { ICDBNode::CTextId textId; node->removeObserver(&_DBAnimalObs, textId); } } pSM->removeSkillChangeCallback(&_SkillObserver); } // *************************************************************************** void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, uint maxSelection) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); CSBrickManager *pBM= CSBrickManager::getInstance(); // fill selection with all bricks of the same family uint i; std::vector bricks; for(i=0;i<_FaberPlanBrickFamilies.size();i++) { const std::vector &famBricks= pBM->getFamilyBricks(_FaberPlanBrickFamilies[i]); bricks.insert(bricks.end(), famBricks.begin(), famBricks.end()); } // get only ones known pBM->filterKnownBricks(bricks); // fill db uint num= min(maxSelection, uint(bricks.size())); for(i=0;igetDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(bricks[i].asInt()); else pIM->getDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(0); } } // *************************************************************************** CItemImage *CActionPhraseFaber::getInvMirrorItemImage(uint slotIndex) { uint invId= slotIndex/MAX_PLAYER_INV_ENTRIES; uint indexInInv= slotIndex%MAX_PLAYER_INV_ENTRIES; // get the item image from bag, steed, pack animal... if(invId==INVENTORIES::bag && indexInInv=INVENTORIES::pet_animal && invIdFamily==ITEMFAMILY::RAW_MATERIAL && getInventory().isInventoryAvailable((INVENTORIES::TInventory)invId); } // *************************************************************************** void CActionPhraseFaber::validateFaberPlanSelection(CSBrickSheet *itemPlanBrick) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); // **** Get the ItemSheet for faber plan. NULL => no op. if(!itemPlanBrick) return; _ExecuteFromItemPlanBrick= itemPlanBrick; // TestYoyo /*for(uint tam=0;tam<_ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps.size();tam++) { _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[tam].Quantity= 20; } _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps.resize(2); _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[0].ItemRequired= CSheetId("m0152chdca01.sitem"); _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[0].Quantity= 13; _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[1].ItemRequired= CSheetId("m0691chdca01.sitem"); _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[1].Quantity= 25; */ // the num of itempPart/specific items to setup _MPBuildNumItemPartReq= min((uint)MAX_ITEM_REQ_LINE, (uint)_ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps.size()); _MPBuildNumSpecificItemReq= min(((uint)MAX_ITEM_REQ_LINE-_MPBuildNumItemPartReq), (uint)_ExecuteFromItemPlanBrick->FaberPlan.FormulaMps.size()); _MPBuildNumTotalItemReq= _MPBuildNumItemPartReq + _MPBuildNumSpecificItemReq; // Setup the selected plan CCDBNodeLeaf *node= pIM->getDbProp(FaberPlanDB, false); if(node) node->setValue32(_ExecuteFromItemPlanBrick->Id.asInt()); // Setup the result item node= pIM->getDbProp(ItemResultSheetDB, false); if(node) node->setValue32(itemPlanBrick->FaberPlan.ItemBuilt.asInt()); // Setup the result quantity (for stacked items) node= pIM->getDbProp(ItemResultQuantityDB, false); if(node) node->setValue32(itemPlanBrick->FaberPlan.NbItemBuilt); // Show the ItemResult group CInterfaceGroup *groupMp= dynamic_cast(pIM->getElementFromId(FaberPhraseItemResultGroup)); if(groupMp) groupMp->setActive(true); // **** reset the mpBuild // For all item required. uint itemReqLine; for(itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++) { CMPBuild &mpBuild= _MPBuild[itemReqLine]; // Type of requirement? // First go the ItemPart reqs if(itemReqLine<_MPBuildNumItemPartReq) { uint itemPartId= itemReqLine; mpBuild.RequirementType= CMPBuild::ItemPartReq; mpBuild.FaberTypeRequired= _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[itemPartId].FaberTypeFilter; mpBuild.QuantityReq= _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[itemPartId].Quantity; } // Then go the Specific item reqs else { uint itemSpecificId= itemReqLine - _MPBuildNumItemPartReq; mpBuild.RequirementType= CMPBuild::SpecificItemReq; mpBuild.SpecificItemRequired= _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[itemSpecificId].ItemRequired; mpBuild.QuantityReq= _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[itemSpecificId].Quantity; } // Reset the quantity setuped for this line mpBuild.NumMpSlot= 0; for(uint mpSlot=0;mpSlotgetSheetID()); CItemSheet *mpSheet= dynamic_cast(SheetMngr.get(sheetId)); if( isMpAvailable(mpSheet, i) ) { _InventoryMirror[i].Sheet= sheetId; _InventoryMirror[i].Quality= itemImage->getQuality(); _InventoryMirror[i].Quantity= itemImage->getQuantity(); _InventoryMirror[i].UserColor= itemImage->getUserColor(); _InventoryMirror[i].Weight= itemImage->getWeight(); // Bkup original quantity from inventory _InventoryMirror[i].OriginalQuantity= _InventoryMirror[i].Quantity; } } } // **** show ItemParts according to plan. // Hide the valid button CCtrlBaseButton *validButton= dynamic_cast(pIM->getElementFromId(FaberPhraseValidButton)); if(validButton) validButton->setFrozen(true); // reset DB, show/hide the Mps for(itemReqLine=0;itemReqLinegetDbProp(toString("%s:%d:%d:SHEET", MPFaberDB.c_str(), itemReqLine, mpSlot), false); if(node) node->setValue32(0); } // Setup item requirement groups CInterfaceGroup *itemReqLineGroup= dynamic_cast(pIM->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) )); if(itemReqLineGroup) { if( itemReqLine<_MPBuildNumTotalItemReq ) { itemReqLineGroup->setActive(true); // Set as Text the required MP FaberType or Specific item CViewText *viewText= dynamic_cast(itemReqLineGroup->getView(FaberPhraseText)); if(viewText) { ucstring text; if(mpBuild.RequirementType==CMPBuild::ItemPartReq) { text= CI18N::get("uihelpFaberMpHeader"); strFindReplace(text, "%f", RM_FABER_TYPE::toLocalString(mpBuild.FaberTypeRequired) ); } else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq) { text= STRING_MANAGER::CStringManagerClient::getItemLocalizedName(mpBuild.SpecificItemRequired); } else { nlstop; } viewText->setText( text ); } // Set as Icon the required MP FaberType / or Sheet Texture (directly...) CViewBitmap *viewBmp= dynamic_cast(itemReqLineGroup->getView(FaberPhraseIcon)); if(viewBmp) { if(mpBuild.RequirementType==CMPBuild::ItemPartReq) { // texture name in config.xml viewBmp->setTexture(pIM->getDefine( RM_FABER_TYPE::toIconDefineString(mpBuild.FaberTypeRequired) )); } else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq) { // NB: the texture is scaled, so it's ok to put the item 40x40 texture const CItemSheet *itemSheet= dynamic_cast(SheetMngr.get(mpBuild.SpecificItemRequired)); if(itemSheet) viewBmp->setTexture(itemSheet->getIconMain()); else viewBmp->setTexture(std::string()); } else { nlstop; } } // update the EmptySlot updateEmptySlot(itemReqLine, itemReqLineGroup); // setup item required quantity view updateQuantityView(itemReqLine); } else { itemReqLineGroup->setActive(false); } } } // **** Setup the new window title CGroupContainer *window= dynamic_cast(pIM->getElementFromId(FaberPhraseWindow)); if(window) { // Setup the Title with the item built ucstring title= CI18N::get("uiPhraseFaberExecute"); strFindReplace(title, "%item", STRING_MANAGER::CStringManagerClient::getItemLocalizedName(_ExecuteFromItemPlanBrick->FaberPlan.ItemBuilt) ); window->setUCTitle (title); } // **** result view updateItemResult(); } // *************************************************************************** void CActionPhraseFaber::resetSelection() { CInterfaceManager *pIM= CInterfaceManager::getInstance(); for(uint i=0;igetDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0); } } // *************************************************************************** void CActionPhraseFaber::fillSelection(const std::vector &mps) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); uint num= min(uint(MAX_MP_SELECTION_ENTRIES), uint(mps.size())); for(uint i=0;igetDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0); } } // *************************************************************************** void CActionPhraseFaber::filterSelectionItemPart(std::vector &mps, RM_FABER_TYPE::TRMFType itemPartFilter, ITEM_ORIGIN::EItemOrigin originFilter) { // Unknown => no fitler if(itemPartFilter==RM_FABER_TYPE::Unknown) return; std::vector res; res.reserve(mps.size()); for(uint i=0;i(SheetMngr.get(_InventoryMirror[mps[i]].Sheet)); // test itemPartFilter match. if(itemSheet) { if(itemSheet->canBuildItemPart(itemPartFilter, originFilter)) { res.push_back(mps[i]); } } } mps= res; } // *************************************************************************** void CActionPhraseFaber::filterSelectionItemSpecific(std::vector &mps, NLMISC::CSheetId specificItemWanted) { std::vector res; res.reserve(mps.size()); // if unknown sheetid, no match if(specificItemWanted==NLMISC::CSheetId::Unknown) { mps.clear(); return; } for(uint i=0;i(SheetMngr.get(_InventoryMirror[mps[i]].Sheet)); // test sheetid match. if(itemSheet) { if(itemSheet->Id == specificItemWanted) { res.push_back(mps[i]); } } } mps= res; } // *************************************************************************** void CActionPhraseFaber::startMpSelection(uint itemReqLine, uint mpSlot) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); // get the ctrlSlot CDBCtrlSheet *ctrlSlot= NULL; CInterfaceGroup *itemReqLineGroup= dynamic_cast(pIM->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) )); if(itemReqLineGroup) { CDBGroupListSheet *listSheet= dynamic_cast(itemReqLineGroup->getGroup(FaberPhraseList)); if(listSheet) ctrlSlot= listSheet->getSheet(mpSlot); } if(!ctrlSlot) return; // get the mpBuild setup nlassert(itemReqLinegetDbProp(MPQuantitySelectDb + ":CUR_QUANTITY", false); if(node) node->setValue32(maxQuantity); node= pIM->getDbProp(MPQuantitySelectDb + ":MAX_QUANTITY", false); if(node) node->setValue32(maxQuantity); // bkup for validation _MpSelectionItemReqLine= itemReqLine; _MpChangeQuantitySlot= mpSlot; // Setup the text with value by default CInterfaceGroup *quantityModal= dynamic_cast(pIM->getElementFromId(FaberPhraseMpQuantityModal)); if(quantityModal) { CGroupEditBox *eb = dynamic_cast(quantityModal->getGroup("eb")); if (eb) { pIM->setCaptureKeyboard(eb); eb->setInputString(toString(maxQuantity)); eb->setSelectionAll(); } } // launch the modal pIM->enableModalWindow(ctrlSlot, quantityModal); } // else select new MP else { // For All the inventory vector selectMps; for(uint i=0;i<_InventoryMirror.size();i++) { // If still some MP on this stack, and if not already selected, add to selection if(_InventoryMirror[i].Quantity>0 && (_InventoryMirror[i].Selected&(1<(SheetMngr.get(_ExecuteFromItemPlanBrick->FaberPlan.ItemBuilt)); ITEM_ORIGIN::EItemOrigin itemOrigin= itemBuilt? itemBuilt->ItemOrigin : ITEM_ORIGIN::UNKNOWN; filterSelectionItemPart(selectMps, mpBuild.FaberTypeRequired, itemOrigin); } else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq) { filterSelectionItemSpecific(selectMps, mpBuild.SpecificItemRequired); } else { nlstop; } // Reset the DB selection resetSelection(); fillSelection(selectMps); // Bkup Selection for Validate later _MpSelectionItemReqLine= itemReqLine; _MpCurrentSelection= selectMps; // Open the Selection Window. pIM->enableModalWindow(ctrlSlot, FaberPhraseMpListModal); } } // *************************************************************************** void CActionPhraseFaber::validateMpSelection(uint selectId) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); if(selectId>=_MpCurrentSelection.size()) { pIM->disableModalWindow(); return; } // get which MP of the inventory we have selected uint newInvSlot= _MpCurrentSelection[selectId]; // get the build uint itemReqLine= _MpSelectionItemReqLine; CMPBuild &mpBuild= _MPBuild[itemReqLine]; // Select the quantity to peek from this inventory slot: get max possible sint quantity= mpBuild.QuantityReq - getTotalQuantitySetuped(itemReqLine); nlassert(quantity>0); quantity= min((sint32)quantity, _InventoryMirror[newInvSlot].Quantity); // it may be possible (by update DB and error) that selected slot is no more usable => just quit if(quantity<=0) return; // And Remove (virtually) item stack from this slot _InventoryMirror[newInvSlot].Quantity-= quantity; // mark as selected for this itemReqLine, so can no more select it _InventoryMirror[newInvSlot].Selected|= 1<disableModalWindow(); // **** when all is correctly ended, open the quantity selection // NB: just enable this code, if you want this feature //startMpSelection(itemReqLine, mpBuild.NumMpSlot-1); } // *************************************************************************** void CActionPhraseFaber::validateMpSelectQuantity() { CInterfaceManager *pIM= CInterfaceManager::getInstance(); // get current execution context of the validate uint itemReqLine= _MpSelectionItemReqLine; nlassert(itemReqLinegetDbProp(MPQuantitySelectDb + ":CUR_QUANTITY", false); if(node) quantitySelected= node->getValue32(); // maximize (if error) quantitySelected= min(quantitySelected, getMaxQuantityChange(itemReqLine, mpSlot)); // if the new quantity is 0 if(quantitySelected==0) { // special: remove the mp slot from list deleteMpSlot(itemReqLine, mpSlot); } else { // restore old quantity into inventory _InventoryMirror[invSlot].Quantity+= mpBuild.QuantitySelected[mpSlot]; // And then Remove (virtually) new item stack from this slot _InventoryMirror[invSlot].Quantity-= quantitySelected; // update the build mpBuild.QuantitySelected[mpSlot]= quantitySelected; // Update The Execution View CItem item= _InventoryMirror[invSlot]; item.Quantity= quantitySelected; fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpSlot), item); } // update the empty slot updateEmptySlot(itemReqLine); // update quantity view updateQuantityView(itemReqLine); // update the valid button updateValidButton(); // update the item result updateItemResult(); // hide the Modal Quantity selection pIM->disableModalWindow(); } // *************************************************************************** void CActionPhraseFaber::validateExecution() { // the plan has must been selected nlassert(_ExecuteFromItemPlanBrick); // Build the list of MP in Bag. vector mpItemPartList; vector specificItemList; // Run all the current Build execution for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++) { CMPBuild &mpBuild= _MPBuild[itemReqLine]; // For all slot setuped. for(uint mpSlot=0;mpSlotexecuteCraft(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex, _ExecuteFromItemPlanBrick->Id.asInt(), mpItemPartList, specificItemList); // Open the Interface to get the crafted item CTempInvManager::getInstance()->open(TEMP_INV_MODE::Craft); // NO more Close the Execution window (allow refaber quick) /*CInterfaceManager *pIM= CInterfaceManager::getInstance(); CInterfaceElement *window= pIM->getElementFromId(FaberPhraseWindow); if(window) window->setActive(false); */ } // *************************************************************************** uint CActionPhraseFaber::getTotalQuantitySetuped(uint itemReqLine) const { nlassert(itemReqLine(pIM->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) )); if(!itemReqLineGroup) return; // get the list sheet and ctrlButton. CDBGroupListSheet *listSheet= dynamic_cast(itemReqLineGroup->getGroup(FaberPhraseList)); if(!listSheet) return; // NB: forceValidity calls invalidateCoords() if state change => dont "clear then set". // All Setuped? bool allSetuped= getTotalQuantitySetuped(itemReqLine) >= _MPBuild[itemReqLine].QuantityReq; // button no more needed? if(allSetuped) { // Reset all ForceValid for(uint i=0;iforceValidity(i, false); } else { // Reset all ForceValid for(uint i=0;iforceValidity(i, i==_MPBuild[itemReqLine].NumMpSlot); } } // Special for Specific Item requirement. Setup grayed item for the last empty slot for(uint i=0;i=mpBuild.NumMpSlot) { CItem item; // If Specfific requirement and just the last one, don't leave empty if(!allSetuped && i==mpBuild.NumMpSlot && mpBuild.RequirementType== CMPBuild::SpecificItemReq) { item.Sheet= mpBuild.SpecificItemRequired; } fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, i), item); } // *** Grayed,NoQuantity,NoQuality for the last slot of a specific requirement CDBCtrlSheet *ctrl= listSheet->getSheet(i); if(ctrl) { if(i==mpBuild.NumMpSlot && mpBuild.RequirementType== CMPBuild::SpecificItemReq) { ctrl->setUseQuality(false); ctrl->setUseQuantity(false); ctrl->setGrayed(true); } else { ctrl->setUseQuality(true); ctrl->setUseQuantity(true); ctrl->setGrayed(false); } } } } // *************************************************************************** void CActionPhraseFaber::updateQuantityView(uint itemReqLine) { nlassert(itemReqLinegetDbProp(toString("%s:%d:SELECTED", MPQuantityDb.c_str(), itemReqLine), false); if(node) node->setValue32(getTotalQuantitySetuped(itemReqLine)); node= pIM->getDbProp(toString("%s:%d:REQUIRED", MPQuantityDb.c_str(), itemReqLine), false); if(node) node->setValue32(_MPBuild[itemReqLine].QuantityReq); } // *************************************************************************** void CActionPhraseFaber::updateValidButton() { // Check For All MPSlot: If All Ok, then can validate! bool canValid= true; // can validate only if the Plan has been selected if(_ExecuteFromItemPlanBrick) { // Run all the current Build execution for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++) { canValid= canValid && getTotalQuantitySetuped(itemReqLine)==_MPBuild[itemReqLine].QuantityReq; } } else canValid= false; // unfreeze if valid CInterfaceManager *pIM= CInterfaceManager::getInstance(); CCtrlBaseButton *validButton= dynamic_cast(pIM->getElementFromId(FaberPhraseValidButton)); if(validButton) validButton->setFrozen(!canValid); } // *************************************************************************** void CActionPhraseFaber::deleteMpSlot(uint itemReqLine, uint mpSlot) { nlassert(itemReqLine(pCaller); if(!ctrl) return; // get itemReqLine to Modify uint itemReqLine; fromString(getParam(Params, "item_req"), itemReqLine); // get mpSlot edited uint mpSlot= ctrl->getIndexInDB(); if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->startMpSelection(itemReqLine, mpSlot); } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberSelectMP, "phrase_faber_select_mp"); // *************************************************************************** class CHandlerPhraseFaberValidateMP : public IActionHandler { public: virtual void execute (CCtrlBase *pCaller, const string &/* Params */) { CDBCtrlSheet *ctrl= dynamic_cast(pCaller); if(!ctrl) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); pIM->disableModalWindow(); return; } // get the selected MP. uint selectMP= ctrl->getIndexInDB(); if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->validateMpSelection(selectMP); } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidateMP, "phrase_faber_validate_mp"); // *************************************************************************** class CHandlerPhraseFaberValidate : public IActionHandler { public: virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->validateExecution(); } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidate, "phrase_faber_validate"); // *************************************************************************** class CHandlerPhraseFaberValidateOnEnter : public IActionHandler { public: virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { // get the button CInterfaceManager *pIM= CInterfaceManager::getInstance(); CCtrlBaseButton *button= dynamic_cast(pIM->getElementFromId(FaberPhraseValidButton)); // Ok, button found. test if active. if( button && !button->getFrozen() ) { // Act as if the player click on this button pIM->runActionHandler("phrase_faber_validate", button ); } } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidateOnEnter, "phrase_faber_validate_on_enter"); // *************************************************************************** class CHandlerPhraseFaberSelectMpQuantity : public IActionHandler { public: virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->validateMpSelectQuantity(); } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberSelectMpQuantity, "phrase_faber_select_mp_quantity"); // *************************************************************************** void launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->launchFaberCastWindow(memoryLine, memoryIndex, rootBrick); } // *************************************************************************** void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->fillFaberPlanSelection(brickDB, maxSelection); } // *************************************************************************** void validateFaberPlanSelection(CSBrickSheet *itemPlanBrick) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->validateFaberPlanSelection(itemPlanBrick); } // *************************************************************************** void closeFaberCastWindow() { if (ActionPhraseFaber == NULL) return; CGroupContainer *window= dynamic_cast(CInterfaceManager::getInstance()->getElementFromId(FaberPhraseWindow)); if(window && window->getActive()) window->setActive(false); } // *************************************************************************** class CHandlerPhraseFaberOnClose : public IActionHandler { public: virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->onCloseFaberCastWindow(); } }; REGISTER_ACTION_HANDLER( CHandlerPhraseFaberOnClose, "phrase_faber_on_close"); // *************************************************************************** // *************************************************************************** // Management of Change in Inventory // *************************************************************************** // *************************************************************************** // *************************************************************************** void CActionPhraseFaber::removeMpSlotThatUseInvSlot(uint invSlot, uint quantityToRemove) { if(quantityToRemove==0) return; // remove from all mpSlots for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++) { CMPBuild &mpBuild= _MPBuild[itemReqLine]; for(uint mpSlot=0;mpSlotgetSheetID()); CItemSheet *mpSheet= dynamic_cast(SheetMngr.get(sheetId)); CItem newInvItem; // The item must be a mp, and the item must be available if( isMpAvailable(mpSheet, i) ) { newInvItem.Sheet= sheetId; newInvItem.Quality= itemImage->getQuality(); newInvItem.Quantity= itemImage->getQuantity(); newInvItem.UserColor= itemImage->getUserColor(); newInvItem.Weight= itemImage->getWeight(); newInvItem.OriginalQuantity= newInvItem.Quantity; } /* There is 4 cases: - no changes => no op. - new Mp on a empty or non Mp slot. Easy, just add. - old Mp removed (not same sheetId/quality/userColor) - old Mp with quantity changed to be greater - old Mp with quantity changed to be smaller */ CItem &curInvItem= _InventoryMirror[i]; // Bkup Id in newInvItem (for ope= correctness) newInvItem.InventoryId= curInvItem.InventoryId; newInvItem.IdInInventory= curInvItem.IdInInventory; // If the item was not a mp if(_InventoryMirror[i].Sheet==CSheetId::Unknown) { // if now it is, easy, just add if(newInvItem.Sheet!=CSheetId::Unknown) curInvItem= newInvItem; } // else must test change or remove else { bool sameMp; sameMp= curInvItem.Sheet == newInvItem.Sheet && curInvItem.Quality == newInvItem.Quality && curInvItem.UserColor == newInvItem.UserColor ; // if the Mp was deleted from this slot, delete it from all faber execution if(!sameMp) { // remove all from current execution removeMpSlotThatUseInvSlot(i, curInvItem.OriginalQuantity); // replace (with nothing or new different Mp) curInvItem= newInvItem; // mpSlot may have been deleted displayChange= true; } // test change of quantity else { // if the quantity is the same, no op! if(curInvItem.OriginalQuantity!=newInvItem.OriginalQuantity) { // if the quantity is now greater, its easy if(newInvItem.OriginalQuantity > curInvItem.OriginalQuantity) { // just add the difference to the original and current setuped quantity uint32 diff= newInvItem.OriginalQuantity - curInvItem.OriginalQuantity; curInvItem.OriginalQuantity+= diff; curInvItem.Quantity+= diff; } else { // complex, must remove the quantity that has changed uint32 diff= curInvItem.OriginalQuantity - newInvItem.OriginalQuantity; // try first to remove it from remaining quantity if(curInvItem.Quantity>=(sint32)diff) { // no change to current mpSlots! curInvItem.Quantity-= diff; // must close the selection modal if opened displayChange= true; } // The remaining quantity is not enough, must also remove from mpSlot that use it! else { uint32 toRemoveFromSlot= diff - curInvItem.Quantity; curInvItem.Quantity= 0; // remove all needed to current mp slot. removeMpSlotThatUseInvSlot(i, toRemoveFromSlot); // mpSlot may have been deleted displayChange= true; } // bkup new original quantity curInvItem.OriginalQuantity= newInvItem.OriginalQuantity; } } } } } } // must update display? if(displayChange) { for(uint itemReqLine= 0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++) { updateEmptySlot(itemReqLine); updateQuantityView(itemReqLine); } updateValidButton(); // close selection modals if they are opened CInterfaceGroup *quantityModal= dynamic_cast(pIM->getElementFromId(FaberPhraseMpQuantityModal)); if(quantityModal && pIM->getModalWindow()==quantityModal) pIM->disableModalWindow(); CInterfaceGroup *listModal= dynamic_cast(pIM->getElementFromId(FaberPhraseMpListModal)); if(listModal && pIM->getModalWindow()==listModal) pIM->disableModalWindow(); // update item result updateItemResult(); } } // *************************************************************************** void CActionPhraseFaber::CDBInventoryObs::update(ICDBNode * /* node */) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->onInventoryChange(); } // *************************************************************************** void CActionPhraseFaber::CDBAnimalObs::update(ICDBNode * /* node */) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; ActionPhraseFaber->onInventoryChange(); } // *************************************************************************** void CActionPhraseFaber::updateItemResult() { CInterfaceManager *pIM= CInterfaceManager::getInstance(); CSPhraseManager *pPM= CSPhraseManager::getInstance(); CSBrickManager *pBM= CSBrickManager::getInstance(); uint i; // level is the min level of MP sint32 minLevel= INT_MAX; // Stat is computed like server float statArray[RM_FABER_STAT_TYPE::NumRMStatType]; float statCount[RM_FABER_STAT_TYPE::NumRMStatType]; uint64 itemStatBF= 0; for(i=0;igetMemorizedPhrase(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex); const CSPhraseCom &phrase= pPM->getPhrase(phraseSlot); uint32 recommendedPropId= pBM->getBrickPropId("cr_recommended"); for(i=0;igetBrick(phrase.Bricks[i]); if(brick) { for(uint j=0;jProperties.size();j++) { // if a CR_RECOMMENDED propId if(brick->Properties[j].PropId == recommendedPropId) { // minimze the level minLevel= min(minLevel, sint32(brick->Properties[j].Value)); } } } } // **** Parse all MPs setuped, to compute level and stats uint totalItemPartMPReq= 0; uint totalItemPartMPSetuped= 0; for(i=0;i<_MPBuildNumTotalItemReq;i++) { CMPBuild &mpBuild= _MPBuild[i]; // --- ItemPart requirement? if(mpBuild.RequirementType==CMPBuild::ItemPartReq) { // For all slots setuped uint nSlot= min((uint)MAX_MP_SLOT, mpBuild.NumMpSlot); for(uint j=0;j(SheetMngr.get(_InventoryMirror[mpBuild.Id[j]].Sheet)); if(mp && mp->canBuildItemPart(mpBuild.FaberTypeRequired)) { // minimize level minLevel= min(_InventoryMirror[mpBuild.Id[j]].Quality, minLevel); // Increment stat for each of this MP selected const CItemSheet::CMpItemPart &mpIP= mp->getItemPart(mpBuild.FaberTypeRequired); // append to the stats uint numMps= mpBuild.QuantitySelected[j]; for(uint k=0;kMp.StatEnergy/100.f); // Increment color stat if(mp->Mp.MpColor>=0 && mp->Mp.MpColorMp.MpColor]+= numMps; } // Total MP setuped totalItemPartMPSetuped+= numMps; } } // get all stat for this item, and count MP req per stat for(uint k=0;k(SheetMngr.get(_InventoryMirror[mpBuild.Id[j]].Sheet)); if(mp->Id == mpBuild.SpecificItemRequired) { // minimize level minLevel= min(_InventoryMirror[mpBuild.Id[j]].Quality, minLevel); // Formula 's Specific MPs don't impact on stats. } } } } // Mean stat for(i=0;i=totalItemPartMPReq); // **** setup Level if(minLevel==INT_MAX) minLevel= 0; CCDBNodeLeaf *node= pIM->getDbProp(ItemResultSheetLevel, false); if(node) node->setValue32(minLevel); // **** change success rate too CViewText *successView= dynamic_cast(pIM->getElementFromId(FaberPhraseFpSuccessText)); if(successView) { ucstring text= CI18N::get("uiPhraseFaberSuccessRate"); // Get the success rate of the related phrase uint phraseSlot= pPM->getMemorizedPhrase(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex); sint32 craftSuccessModifier = 0; CCDBNodeLeaf *nodeCSM= pIM->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:CRAFT", false); if(nodeCSM) { craftSuccessModifier = nodeCSM->getValue32(); } // With the faber plan skill sint success= pPM->getCraftPhraseSuccessRate(pPM->getPhrase(phraseSlot), _ExecuteFromItemPlanBrick->getSkill(), minLevel); string successStr; if( craftSuccessModifier == 0 ) { successStr = toString("@{FFFF}") + toString(success); } else if( craftSuccessModifier > 0 ) // bonus { successStr = "@{0F0F}" + toString(success+craftSuccessModifier) + "@{FFFF}(" + toString( success ) + "@{0F0F} + " + toString( craftSuccessModifier ) + "@{FFFF})"; } else { successStr = "@{E42F}" + toString(success+craftSuccessModifier) + "@{FFFF}(" + toString( success ) + "@{E42F} - " + toString( craftSuccessModifier ) + "@{FFFF})"; } strFindReplace(text, "%success", successStr ); successView->setTextFormatTaged(text); } // **** setup Color // Same than server code (NB: beige==1 per default) uint maxNumColor = 0; uint dominanteColor = 1; for(i = 0; i < RM_COLOR::NumColors; ++i ) { if( bestItemColor[i] > maxNumColor ) { maxNumColor = bestItemColor[i]; dominanteColor = i; } } node= pIM->getDbProp(ItemResultSheetColor, false); if(node) node->setValue32(dominanteColor); // **** Get Stat validity uint64 itemStatFinalUsageBF= 0; // Some stat (magic protection and magic resist) are finaly used in the item only for the best ones itemStatFinalUsageBF= RM_FABER_STAT_TYPE::getStatFinalValidity(statArray, itemStatBF); // **** Stats CInterfaceGroup *groupMp= dynamic_cast(pIM->getElementFromId(FaberPhraseItemResultGroup)); if(groupMp) { // default: hide all for(i=0;i(groupMp->getElement(groupMp->getId()+toString(":stat%d",i) )); if(groupStat) groupStat->setActive(false); } // enable only one that are relevant for this item uint groupIndex= 0; for(i=0;i(groupMp->getElement(groupMp->getId()+toString(":stat%d",groupIndex) )); if(groupStat) { groupStat->setActive(true); // fill text and bar according to stat CViewText *statTitle= dynamic_cast(groupStat->getElement(groupStat->getId()+":text" )); CDBViewBar *statValueBar= dynamic_cast(groupStat->getElement(groupStat->getId()+":bar" )); CViewText *statValueText= dynamic_cast(groupStat->getElement(groupStat->getId()+":textstat" )); CCtrlBase *statToolTip= dynamic_cast(groupStat->getElement(groupStat->getId()+":tt" )); uint sv= uint(statArray[i]*100); if(statTitle) { statTitle->setText(RM_FABER_STAT_TYPE::toLocalString(statType)); statTitle->setColor(usageColor); } if(statValueBar) { statValueBar->setValue(sv); statValueBar->setColor(usageColor); } if(statValueText) { statValueText->setText(toString(sv)+"/100"); statValueText->setColor(usageColor); } if(statToolTip) { if(finalyUsed) { // display something only for magic/protect stat if( RM_FABER_STAT_TYPE::isMagicResistStat(RM_FABER_STAT_TYPE::TRMStatType(i)) || RM_FABER_STAT_TYPE::isMagicProtectStat(RM_FABER_STAT_TYPE::TRMStatType(i)) ) statToolTip->setDefaultContextHelp(CI18N::get("uiFaberStatActive")); else statToolTip->setDefaultContextHelp(ucstring()); } else statToolTip->setDefaultContextHelp(CI18N::get("uiFaberStatGrayed")); } } groupIndex++; } } // **** BestStat (for text over) node= pIM->getDbProp(ItemResultSheetStatType, false); if(node) { float bestStatValue =-1.0f; RM_FABER_STAT_TYPE::TRMStatType bestStat = RM_FABER_STAT_TYPE::NumRMStatType; for( i = 0; i < RM_FABER_STAT_TYPE::NumRMStatType; ++i ) { // if this stat is not relevant for the item, don't use it! if( (itemStatBF&(uint64(1)< bestStatValue ) { bestStatValue = value; bestStat = (RM_FABER_STAT_TYPE::TRMStatType)i; } } // Setup DB node->setValue32(bestStat); } // **** ClassType (for text over) node= pIM->getDbProp(ItemResultSheetClassType, false); if(node) { // Setup DB node->setValue32(RM_CLASS_TYPE::getItemClass((uint32)(100.0f * statEnergy))); } } /* Handle change of skill -> recompute success rate */ void CActionPhraseFaber::CSkillObserver::onSkillChange() { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; // Dont update if the plan has not yet been selected if(ActionPhraseFaber->_ExecuteFromItemPlanBrick==NULL) return; ActionPhraseFaber->updateItemResult(); }