Added spawnCrystalItem
[ryzomcore.git] / ryzom / client / src / interface_v3 / action_phrase_faber.cpp
blobb9f08cfc2566efdd547195339823c39eff4e4f2f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 //
9 // This program is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Affero General Public License as
11 // published by the Free Software Foundation, either version 3 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Affero General Public License for more details.
19 // You should have received a copy of the GNU Affero General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "stdpch.h"
25 #include "action_phrase_faber.h"
26 #include "../client_sheets/sbrick_sheet.h"
27 #include "interface_manager.h"
28 #include "../sheet_manager.h"
29 #include "inventory_manager.h"
30 #include "nel/gui/action_handler.h"
31 #include "../client_cfg.h"
32 #include "nel/gui/ctrl_base_button.h"
33 #include "nel/gui/group_container.h"
34 #include "../string_manager_client.h"
35 #include "../net_manager.h"
36 #include "sbrick_manager.h"
37 #include "sphrase_manager.h"
38 #include "nel/gui/group_editbox.h"
39 #include "nel/gui/dbview_bar.h"
40 #include "skill_manager.h"
41 #include "game_share/bot_chat_types.h"
43 #ifdef DEBUG_NEW
44 #define new DEBUG_NEW
45 #endif
47 using namespace std;
48 using namespace NLMISC;
51 // ***************************************************************************
52 const std::string FaberPlanDB= "UI:PHRASE:FABER:FABER_PLAN:SHEET";
53 const std::string FaberPlanHpBuffDB= "UI:PHRASE:FABER:FABER_PLAN:HP_BUFF";
54 const std::string FaberPlanSapBuffDB= "UI:PHRASE:FABER:FABER_PLAN:SAP_BUFF";
55 const std::string FaberPlanStaBuffDB= "UI:PHRASE:FABER:FABER_PLAN:STA_BUFF";
56 const std::string FaberPlanFocusBuffDB= "UI:PHRASE:FABER:FABER_PLAN:FOCUS_BUFF";
57 const std::string MPFaberDB= "UI:PHRASE:FABER:MP_BUILD";
58 const std::string MPSelectionDB= "UI:PHRASE:FABER:MP_SELECT";
59 const std::string MPQuantityDb= "UI:PHRASE:FABER:MP_QUANTITY";
60 const std::string MPQuantitySelectDb= "UI:PHRASE:FABER:STACK_SELECT";
61 const std::string ItemResultSheetDB= "UI:PHRASE:FABER:RESULT_ITEM:SHEET";
62 const std::string ItemResultQuantityDB= "UI:PHRASE:FABER:RESULT_ITEM:QUANTITY";
63 const std::string ItemResultSheetLevel= "UI:PHRASE:FABER:RESULT_ITEM:QUALITY";
64 const std::string ItemResultSheetColor= "UI:PHRASE:FABER:RESULT_ITEM:USER_COLOR";
65 const std::string ItemResultSheetClassType= "UI:PHRASE:FABER:RESULT_ITEM:RM_CLASS_TYPE";
66 const std::string ItemResultSheetStatType= "UI:PHRASE:FABER:RESULT_ITEM:RM_FABER_STAT_TYPE";
67 const std::string FaberPhraseWindow= "ui:interface:phrase_faber_execution";
68 const std::string FaberPhraseItemReqLine= FaberPhraseWindow + ":header_opened:item_reqs:item_req_%d";
69 const std::string FaberPhraseList= "list";
70 const std::string FaberPhraseText= "text";
71 const std::string FaberPhraseIcon= "icon";
72 const std::string FaberPhraseValidButton= FaberPhraseWindow + ":header_opened:ok_cancel:ok";
73 const std::string FaberPhraseFpCtrl= FaberPhraseWindow + ":header_opened:faber_plan";
74 const std::string FaberPhraseFpSuccessText= FaberPhraseWindow + ":header_opened:success_text";
75 const std::string FaberPhraseMpListModal= "ui:interface:phrase_faber_mp_selection";
76 const std::string FaberPhraseMpQuantityModal= "ui:interface:phrase_faber_mp_quantity";
77 const std::string FaberPhraseItemResultGroup= FaberPhraseWindow + ":header_opened:item_result";
80 #define MAX_MP_SELECTION_ENTRIES 256
82 // ***************************************************************************
83 CActionPhraseFaber::CActionPhraseFaber()
85 uint size = MAX_PLAYER_INV_ENTRIES + (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL) +
86 MAX_GUILDINV_ENTRIES + MAX_ROOMINV_ENTRIES;
87 _InventoryMirror.resize(size);
88 _InventoryObsSetup= false;
89 _ExecuteFromItemPlanBrick= NULL;
93 // ***************************************************************************
94 void CActionPhraseFaber::fillDBWithMP(const std::string &sheetBase, const CItem &item)
96 CInterfaceManager *pIM= CInterfaceManager::getInstance();
98 NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":SHEET")->setValue32(item.Sheet.asInt());
99 NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":QUALITY")->setValue32(item.Quality);
100 NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":QUANTITY")->setValue32(item.Quantity);
101 NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":USER_COLOR")->setValue32(item.UserColor);
102 NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":WEIGHT")->setValue32(item.Weight);
106 // ***************************************************************************
107 void CActionPhraseFaber::launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick)
109 // **** Get the ItemSheet for faber plan. NULL => no op.
110 if(!rootBrick)
111 return;
112 // Copy Execution launch
113 _ExecuteFromMemoryLine= memoryLine;
114 _ExecuteFromMemoryIndex= memoryIndex;
115 // no item plan setuped for now
116 _ExecuteFromItemPlanBrick= NULL;
119 // get the family of item plan (for selection) from the rootBrick. It is stored in the Property0.
120 _FaberPlanBrickFamilies.clear();
122 if (!rootBrick->Properties.empty())
124 string prop= NLMISC::toUpperAscii(rootBrick->Properties[0].Text);
125 vector<string> strList;
126 splitString(prop, " ", strList);
127 // The prop Id should be 'FPLAN:'
128 if(strList.size()>=2 && strList[0]=="FPLAN:")
130 for(uint i=1;i<strList.size();i++)
132 BRICK_FAMILIES::TBrickFamily bfam= BRICK_FAMILIES::toSBrickFamily(strList[i]);
133 if(bfam!=BRICK_FAMILIES::Unknown)
134 _FaberPlanBrickFamilies.push_back(bfam);
138 // if not found, error, cannot choose the faber plan
139 if(_FaberPlanBrickFamilies.empty())
141 nlwarning("ERROR: The Craft Root %s does not contain a valid FPLAN: property -> Can't select plan to craft",
142 rootBrick->Id.toString().c_str() );
143 return;
147 // **** Hide all widgets, MP Ctrls, and reset DB, until the Plan is not selected
148 CInterfaceManager *pIM= CInterfaceManager::getInstance();
149 // Hide the valid button
150 CCtrlBaseButton *validButton= dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
151 if(validButton)
152 validButton->setFrozen(true);
154 // reset DB, hide the Mps
155 uint itemReqLine;
156 for(itemReqLine=0;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
158 // Reset All Mps slots.
159 for(uint mpSlot=0;mpSlot<MAX_MP_SLOT;mpSlot++)
161 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:%d:SHEET", MPFaberDB.c_str(), itemReqLine, mpSlot), false);
162 if(node)
163 node->setValue32(0);
166 // Hide item requirements groups per default
167 CInterfaceGroup *itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
168 if(itemReqLineGroup)
169 itemReqLineGroup->setActive(false);
172 // Reset the selected plan
173 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanDB, false);
174 if(node)
175 node->setValue32(0);
177 // Reset buff values
178 node = NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanHpBuffDB, false);
179 if (node) node->setValue32(0);
180 node = NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanSapBuffDB, false);
181 if (node) node->setValue32(0);
182 node = NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanStaBuffDB, false);
183 if (node) node->setValue32(0);
184 node = NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanFocusBuffDB, false);
185 if (node) node->setValue32(0);
187 // Reset the result item
188 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetDB, false);
189 if(node)
190 node->setValue32(0);
192 // Hide the ItemResult group
193 CInterfaceGroup *groupMp= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseItemResultGroup));
194 if(groupMp)
195 groupMp->setActive(false);
198 // **** Open the window!
199 CGroupContainer *window= dynamic_cast<CGroupContainer*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseWindow));
200 if(window)
202 window->setActive(true);
204 // Setup the Title with a default text
205 string title= CI18N::get("uiPhraseFaberExecuteNoPlan");
206 window->setTitle (title);
209 // **** setup DB observer!
210 // ensure remove (if setuped before), then add
211 CCDBNodeBranch *branch;
212 branch= NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:INVENTORY:BAG");
213 if(branch) NLGUI::CDBManager::getInstance()->removeBranchObserver( "LOCAL:INVENTORY:BAG",&_DBInventoryObs);
214 if(branch) NLGUI::CDBManager::getInstance()->addBranchObserver( "LOCAL:INVENTORY:BAG",&_DBInventoryObs);
216 // and for all pack animals
217 uint i;
218 for(i=0;i<MAX_INVENTORY_ANIMAL;i++)
220 branch= NLGUI::CDBManager::getInstance()->getDbBranch(toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i));
221 if(branch) NLGUI::CDBManager::getInstance()->removeBranchObserver( toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i).c_str(), &_DBInventoryObs);
222 if(branch) NLGUI::CDBManager::getInstance()->addBranchObserver( toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i).c_str(), &_DBInventoryObs);
225 // Add observers on animal status, cause inventory may become unavailabe during the process
226 for(i=0;i<MAX_INVENTORY_ANIMAL;i++)
228 node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS",i), false);
229 if(node)
231 ICDBNode::CTextId textId;
232 node->addObserver(&_DBAnimalObs, textId);
236 // Observe skill status change to update success rate
237 CSkillManager *pSM= CSkillManager::getInstance();
238 pSM->appendSkillChangeCallback(&_SkillObserver);
241 // add observer to mod_craft_success.sbrick
243 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:CRAFT", false);
244 if (node)
246 ICDBNode::CTextId textId;
247 node->addObserver(&_DBModCraftObs, textId);
253 // ***************************************************************************
254 void CActionPhraseFaber::onCloseFaberCastWindow()
256 CInterfaceManager *pIM= CInterfaceManager::getInstance();
257 CSkillManager *pSM= CSkillManager::getInstance();
259 // No more need to listen inventory change
260 CCDBNodeBranch *branch;
261 branch= NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:INVENTORY:BAG");
262 if(branch) branch->removeBranchObserver(&_DBInventoryObs);
263 // and for all pack animals
264 for(uint i=0;i<MAX_INVENTORY_ANIMAL;i++)
266 branch= NLGUI::CDBManager::getInstance()->getDbBranch(toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i));
267 if(branch) branch->removeBranchObserver(&_DBInventoryObs);
270 // remove observers on animal status, cause inventory may become unavailabe during the process
271 for(uint i=0;i<MAX_INVENTORY_ANIMAL;i++)
273 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS",i), false);
274 if(node)
276 ICDBNode::CTextId textId;
277 node->removeObserver(&_DBAnimalObs, textId);
281 pSM->removeSkillChangeCallback(&_SkillObserver);
284 // remove mod_craft_success.sbrick observer
286 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:CRAFT", false);
287 if (node)
289 ICDBNode::CTextId textId;
290 node->removeObserver(&_DBModCraftObs, textId);
296 // ***************************************************************************
297 void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType)
299 CInterfaceManager *pIM= CInterfaceManager::getInstance();
300 CSBrickManager *pBM= CSBrickManager::getInstance();
302 // fill selection with all bricks of the same family
303 uint i;
304 std::vector<CSheetId> bricks;
305 for(i=0;i<_FaberPlanBrickFamilies.size();i++)
307 const std::vector<NLMISC::CSheetId> &famBricks= pBM->getFamilyBricks(_FaberPlanBrickFamilies[i]);
308 if (toolType == TOOL_TYPE::Unknown)
310 bricks.insert(bricks.end(), famBricks.begin(), famBricks.end());
312 else
314 for(std::vector<CSheetId>::const_iterator it = famBricks.begin(); it != famBricks.end(); ++it)
316 CSBrickSheet *brick= pBM->getBrick(*it);
317 if (brick && brick->FaberPlan.ToolType == toolType)
319 bricks.push_back(*it);
325 // get only ones known
326 pBM->filterKnownBricks(bricks);
328 // fill db
329 uint num= min(maxSelection, uint(bricks.size()));
330 for(i=0;i<maxSelection;i++)
332 if(i<num)
333 NLGUI::CDBManager::getInstance()->getDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(bricks[i].asInt());
334 else
335 NLGUI::CDBManager::getInstance()->getDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(0);
339 // ***************************************************************************
340 CItemImage *CActionPhraseFaber::getInvMirrorItemImage(uint slotIndex, uint& invId, uint& indexInInv)
342 if (slotIndex < MAX_PLAYER_INV_ENTRIES)
344 invId = INVENTORIES::bag;
345 indexInInv = slotIndex;
346 return &getInventory().getBagItem(slotIndex);
348 slotIndex -= MAX_PLAYER_INV_ENTRIES;
350 if (slotIndex < (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL))
352 uint animal = slotIndex / MAX_ANIMALINV_ENTRIES;
353 uint index = slotIndex % MAX_ANIMALINV_ENTRIES;
354 invId = INVENTORIES::pet_animal + animal;
355 indexInInv = index;
356 return &getInventory().getPAItem(animal, index);
358 slotIndex -= (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL);
360 if (slotIndex < MAX_GUILDINV_ENTRIES)
362 if (getInventory().isInventoryAvailable(INVENTORIES::guild))
364 CInterfaceManager *im = CInterfaceManager::getInstance();
365 CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch(SERVER_INVENTORY ":GUILD:" + toString(slotIndex));
366 static CItemImage image;
367 image.build(itemBranch);
368 invId = INVENTORIES::guild;
369 indexInInv = slotIndex;
370 return &image;
372 return NULL;
374 slotIndex -= MAX_GUILDINV_ENTRIES;
376 if (slotIndex < MAX_ROOMINV_ENTRIES)
378 if (getInventory().isInventoryAvailable(INVENTORIES::player_room))
380 CInterfaceManager *im = CInterfaceManager::getInstance();
381 CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch(SERVER_INVENTORY ":ROOM:" + toString(slotIndex));
382 static CItemImage image;
383 image.build(itemBranch);
384 invId = INVENTORIES::player_room;
385 indexInInv = slotIndex;
386 return &image;
388 return NULL;
391 return NULL;
395 // ***************************************************************************
396 bool CActionPhraseFaber::isMpAvailable(CItemSheet *mpSheet, uint invId, uint slotIndex) const
398 return mpSheet && mpSheet->Family==ITEMFAMILY::RAW_MATERIAL && getInventory().isInventoryAvailable((INVENTORIES::TInventory)invId);
401 // ***************************************************************************
402 void CActionPhraseFaber::validateFaberPlanSelection(CSBrickSheet *itemPlanBrick)
404 CInterfaceManager *pIM= CInterfaceManager::getInstance();
407 // **** Get the ItemSheet for faber plan. NULL => no op.
408 if(!itemPlanBrick)
409 return;
410 _ExecuteFromItemPlanBrick= itemPlanBrick;
413 // TestYoyo
414 /*for(uint tam=0;tam<_ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps.size();tam++)
416 _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[tam].Quantity= 20;
418 _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps.resize(2);
419 _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[0].ItemRequired= CSheetId("m0152chdca01.sitem");
420 _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[0].Quantity= 13;
421 _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[1].ItemRequired= CSheetId("m0691chdca01.sitem");
422 _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[1].Quantity= 25;
426 // the num of itempPart/specific items to setup
427 _MPBuildNumItemPartReq= min((uint)MAX_ITEM_REQ_LINE, (uint)_ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps.size());
428 _MPBuildNumSpecificItemReq= min(((uint)MAX_ITEM_REQ_LINE-_MPBuildNumItemPartReq), (uint)_ExecuteFromItemPlanBrick->FaberPlan.FormulaMps.size());
429 _MPBuildNumTotalItemReq= _MPBuildNumItemPartReq + _MPBuildNumSpecificItemReq;
432 // Setup the selected plan
433 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanDB, false);
434 if(node)
435 node->setValue32(_ExecuteFromItemPlanBrick->Id.asInt());
437 // Setup the result item
438 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetDB, false);
439 if(node)
440 node->setValue32(itemPlanBrick->FaberPlan.ItemBuilt.asInt());
442 // Setup the result quantity (for stacked items)
443 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultQuantityDB, false);
444 if(node)
445 node->setValue32(itemPlanBrick->FaberPlan.NbItemBuilt);
447 // Show the ItemResult group
448 CInterfaceGroup *groupMp= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseItemResultGroup));
449 if(groupMp)
450 groupMp->setActive(true);
453 // **** reset the mpBuild
454 // For all item required.
455 uint itemReqLine;
456 for(itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++)
458 CMPBuild &mpBuild= _MPBuild[itemReqLine];
460 // Type of requirement?
461 // First go the ItemPart reqs
462 if(itemReqLine<_MPBuildNumItemPartReq)
464 uint itemPartId= itemReqLine;
465 mpBuild.RequirementType= CMPBuild::ItemPartReq;
466 mpBuild.FaberTypeRequired= _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[itemPartId].FaberTypeFilter;
467 mpBuild.QuantityReq= _ExecuteFromItemPlanBrick->FaberPlan.ItemPartMps[itemPartId].Quantity;
469 // Then go the Specific item reqs
470 else
472 uint itemSpecificId= itemReqLine - _MPBuildNumItemPartReq;
473 mpBuild.RequirementType= CMPBuild::SpecificItemReq;
474 mpBuild.SpecificItemRequired= _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[itemSpecificId].ItemRequired;
475 mpBuild.QuantityReq= _ExecuteFromItemPlanBrick->FaberPlan.FormulaMps[itemSpecificId].Quantity;
478 // Reset the quantity setuped for this line
479 mpBuild.NumMpSlot= 0;
480 for(uint mpSlot=0;mpSlot<MAX_MP_SLOT;mpSlot++)
482 mpBuild.Id[mpSlot]= 0; // useless, but for consistency
483 mpBuild.QuantitySelected[mpSlot]= 0;
486 // reset other to 0 also
487 for(;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
489 _MPBuild[itemReqLine].reset();
493 // **** First clear and copy the inventory to local struct
494 uint i;
495 for(i=0;i<_InventoryMirror.size();i++)
497 _InventoryMirror[i].reset();
500 uint invId = 0;
501 uint indexInInv = 0;
502 // Run all the inventories.
503 for(i=0;i<_InventoryMirror.size();i++)
505 CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv);
506 bool bLockedByOwner = itemImage && itemImage->getLockedByOwner();
507 // item found and not locked?
508 if(itemImage)
510 // setup the origin
511 _InventoryMirror[i].InventoryId= invId;
512 _InventoryMirror[i].IdInInventory= indexInInv;
514 // The item must be a mp
515 CSheetId sheetId= CSheetId(itemImage->getSheetID());
516 CItemSheet *mpSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(sheetId));
517 if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner)
519 _InventoryMirror[i].Sheet= sheetId;
520 _InventoryMirror[i].Quality= itemImage->getQuality();
521 _InventoryMirror[i].Quantity= itemImage->getQuantity();
522 _InventoryMirror[i].UserColor= itemImage->getUserColor();
523 _InventoryMirror[i].Weight= itemImage->getWeight();
524 // Bkup original quantity from inventory
525 _InventoryMirror[i].OriginalQuantity= _InventoryMirror[i].Quantity;
526 _InventoryMirror[i].LockedByOwner= bLockedByOwner;
527 // remember these for sorting
528 _InventoryMirror[i].MpFamily = mpSheet->Mp.Family;
529 _InventoryMirror[i].Ecosystem = mpSheet->Mp.Ecosystem;
530 _InventoryMirror[i].StatEnergy = mpSheet->Mp.StatEnergy;
536 // **** show ItemParts according to plan.
537 // Hide the valid button
538 CCtrlBaseButton *validButton= dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
539 if(validButton)
540 validButton->setFrozen(true);
542 // reset DB, show/hide the Mps
543 for(itemReqLine=0;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
545 CMPBuild &mpBuild= _MPBuild[itemReqLine];
547 // Reset All Mps slots.
548 for(uint mpSlot=0;mpSlot<MAX_MP_SLOT;mpSlot++)
550 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:%d:SHEET", MPFaberDB.c_str(), itemReqLine, mpSlot), false);
551 if(node)
552 node->setValue32(0);
555 // Setup item requirement groups
556 CInterfaceGroup *itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
557 if(itemReqLineGroup)
559 if( itemReqLine<_MPBuildNumTotalItemReq )
561 itemReqLineGroup->setActive(true);
563 // Set as Text the required MP FaberType or Specific item
564 CViewText *viewText= dynamic_cast<CViewText*>(itemReqLineGroup->getView(FaberPhraseText));
565 if(viewText)
567 string text;
568 if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
570 text= CI18N::get("uihelpFaberMpHeader");
571 strFindReplace(text, "%f", RM_FABER_TYPE::toLocalString(mpBuild.FaberTypeRequired) );
573 else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
575 text= STRING_MANAGER::CStringManagerClient::getItemLocalizedName(mpBuild.SpecificItemRequired);
577 else
579 nlstop;
581 viewText->setText(text);
584 // Set as Icon the required MP FaberType / or Sheet Texture (directly...)
585 CViewBitmap *viewBmp= dynamic_cast<CViewBitmap*>(itemReqLineGroup->getView(FaberPhraseIcon));
586 if(viewBmp)
588 if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
590 // texture name in config.xml
591 viewBmp->setTexture(CWidgetManager::getInstance()->getParser()->getDefine( RM_FABER_TYPE::toIconDefineString(mpBuild.FaberTypeRequired) ));
593 else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
595 // NB: the texture is scaled, so it's ok to put the item 40x40 texture
596 const CItemSheet *itemSheet= dynamic_cast<const CItemSheet*>(SheetMngr.get(mpBuild.SpecificItemRequired));
597 if(itemSheet)
598 viewBmp->setTexture(itemSheet->getIconMain());
599 else
600 viewBmp->setTexture(std::string());
602 else
604 nlstop;
608 // update the EmptySlot
609 updateEmptySlot(itemReqLine, itemReqLineGroup);
611 // setup item required quantity view
612 updateQuantityView(itemReqLine);
614 else
616 itemReqLineGroup->setActive(false);
622 // **** Setup the new window title
623 CGroupContainer *window= dynamic_cast<CGroupContainer*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseWindow));
624 if(window)
626 // Setup the Title with the item built
627 string title= CI18N::get("uiPhraseFaberExecute");
628 strFindReplace(title, "%item", STRING_MANAGER::CStringManagerClient::getItemLocalizedName(_ExecuteFromItemPlanBrick->FaberPlan.ItemBuilt) );
629 window->setTitle (title);
633 // **** result view
634 updateItemResult();
637 // ***************************************************************************
638 void CActionPhraseFaber::resetSelection()
640 CInterfaceManager *pIM= CInterfaceManager::getInstance();
642 for(uint i=0;i<MAX_MP_SELECTION_ENTRIES;i++)
644 NLGUI::CDBManager::getInstance()->getDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0);
648 // ***************************************************************************
649 void CActionPhraseFaber::fillSelection(const std::vector<uint> &mps)
651 CInterfaceManager *pIM= CInterfaceManager::getInstance();
653 uint num= min(uint(MAX_MP_SELECTION_ENTRIES), uint(mps.size()));
654 for(uint i=0;i<MAX_MP_SELECTION_ENTRIES;i++)
656 if(i<num && mps[i]<_InventoryMirror.size())
658 CItem &item= _InventoryMirror[mps[i]];
659 fillDBWithMP(MPSelectionDB+ ":" + toString(i), item);
661 else
662 NLGUI::CDBManager::getInstance()->getDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0);
667 // ***************************************************************************
668 void CActionPhraseFaber::filterSelectionItemPart(std::vector<uint> &mps, RM_FABER_TYPE::TRMFType itemPartFilter, ITEM_ORIGIN::EItemOrigin originFilter)
670 // Unknown => no fitler
671 if(itemPartFilter==RM_FABER_TYPE::Unknown)
672 return;
674 std::vector<uint> res;
675 res.reserve(mps.size());
677 for(uint i=0;i<mps.size();i++)
679 // get the item sheet
680 const CItemSheet *itemSheet= dynamic_cast<const CItemSheet*>(SheetMngr.get(_InventoryMirror[mps[i]].Sheet));
681 // test itemPartFilter match.
682 if(itemSheet)
684 if(itemSheet->canBuildItemPart(itemPartFilter, originFilter))
686 res.push_back(mps[i]);
691 mps= res;
695 // ***************************************************************************
696 void CActionPhraseFaber::filterSelectionItemSpecific(std::vector<uint> &mps, NLMISC::CSheetId specificItemWanted)
698 std::vector<uint> res;
699 res.reserve(mps.size());
701 // if unknown sheetid, no match
702 if(specificItemWanted==NLMISC::CSheetId::Unknown)
704 mps.clear();
705 return;
708 for(uint i=0;i<mps.size();i++)
710 // get the item sheet
711 const CItemSheet *itemSheet= dynamic_cast<const CItemSheet*>(SheetMngr.get(_InventoryMirror[mps[i]].Sheet));
712 // test sheetid match.
713 if(itemSheet)
715 if(itemSheet->Id == specificItemWanted)
717 res.push_back(mps[i]);
722 mps= res;
725 // ***************************************************************************
726 struct SItemSortData
728 uint Index;
729 RM_FAMILY::TRMFamily MpFamily;
730 ECOSYSTEM::EECosystem Ecosystem;
731 uint16 StatEnergy;
732 uint16 Quality;
733 uint16 Quantity;
735 SItemSortData(uint i, RM_FAMILY::TRMFamily mpfam, ECOSYSTEM::EECosystem eco, uint16 stat, uint16 quality, uint16 quantity)
736 : Index(i), MpFamily(mpfam), Ecosystem(eco), StatEnergy(stat), Quality(quality), Quantity(quantity)
739 bool operator<(const SItemSortData &other) const
741 // anete, shu
742 if (MpFamily < other.MpFamily) return true;
743 if (MpFamily > other.MpFamily) return false;
745 // desert, lake
746 if (Ecosystem < other.Ecosystem) return true;
747 if (Ecosystem > other.Ecosystem) return false;
749 // basic, choice
750 if (StatEnergy < other.StatEnergy) return true;
751 if (StatEnergy > other.StatEnergy) return false;
753 // q10, q200
754 if (Quality < other.Quality) return true;
755 if (Quality > other.Quality) return false;
757 // x10, x200
758 if (Quantity < other.Quantity) return true;
759 if (Quantity > other.Quantity) return false;
761 return false;
765 // ***************************************************************************
766 void CActionPhraseFaber::sortSelection(std::vector<uint> &mps) const
768 std::vector<SItemSortData> items;
769 items.reserve(mps.size());
770 for(uint i = 0; i < mps.size(); i++)
772 const CItem &item = _InventoryMirror[mps[i]];
773 items.push_back(SItemSortData(mps[i], item.MpFamily, item.Ecosystem, item.StatEnergy, item.Quality, item.Quantity));
776 std::sort(items.begin(), items.end());
778 // overwrite previous values
779 for(uint i = 0; i< items.size(); i++)
780 mps[i] = items[i].Index;
783 // ***************************************************************************
784 void CActionPhraseFaber::startMpSelection(uint itemReqLine, uint mpSlot)
786 CInterfaceManager *pIM= CInterfaceManager::getInstance();
788 // get the ctrlSlot
789 CDBCtrlSheet *ctrlSlot= NULL;
790 CInterfaceGroup *itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
791 if(itemReqLineGroup)
793 CDBGroupListSheet *listSheet= dynamic_cast<CDBGroupListSheet*>(itemReqLineGroup->getGroup(FaberPhraseList));
794 if(listSheet)
795 ctrlSlot= listSheet->getSheet(mpSlot);
797 if(!ctrlSlot)
798 return;
800 // get the mpBuild setup
801 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
802 CMPBuild &mpBuild= _MPBuild[itemReqLine];
804 // If the slot selected is already filled, Launch the MP Quantity selection modal
805 if(mpSlot<mpBuild.NumMpSlot)
807 // fill the sheet info
808 uint invSlot= mpBuild.Id[mpSlot];
809 CItem item= _InventoryMirror[invSlot];
810 fillDBWithMP(MPQuantitySelectDb, item);
812 // compute the maximum quantity possible to fill
813 uint maxQuantity= getMaxQuantityChange(itemReqLine, mpSlot);
815 // set the max quantity as the default quantity to set up.
816 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(MPQuantitySelectDb + ":CUR_QUANTITY", false);
817 if(node) node->setValue32(maxQuantity);
818 node= NLGUI::CDBManager::getInstance()->getDbProp(MPQuantitySelectDb + ":MAX_QUANTITY", false);
819 if(node) node->setValue32(maxQuantity);
821 // bkup for validation
822 _MpSelectionItemReqLine= itemReqLine;
823 _MpChangeQuantitySlot= mpSlot;
825 // Setup the text with value by default
826 CInterfaceGroup *quantityModal= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpQuantityModal));
827 if(quantityModal)
829 CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(quantityModal->getGroup("eb"));
830 if (eb)
832 CWidgetManager::getInstance()->setCaptureKeyboard(eb);
833 eb->setInputString(toString(maxQuantity));
834 eb->setSelectionAll();
838 // launch the modal
839 CWidgetManager::getInstance()->enableModalWindow(ctrlSlot, quantityModal);
841 // else select new MP
842 else
844 // For All the inventory
845 vector<uint> selectMps;
846 for(uint i=0;i<_InventoryMirror.size();i++)
848 // If still some MP on this stack, and if not already selected, add to selection
849 if(_InventoryMirror[i].Quantity>0 && (_InventoryMirror[i].Selected&(1<<itemReqLine))==0 )
851 selectMps.push_back(i);
855 // Filter the selection whether it is an itemPart or specificItem reqiurement
856 if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
858 CItemSheet *itemBuilt= dynamic_cast<CItemSheet*>(SheetMngr.get(_ExecuteFromItemPlanBrick->FaberPlan.ItemBuilt));
859 ITEM_ORIGIN::EItemOrigin itemOrigin= itemBuilt? itemBuilt->ItemOrigin : ITEM_ORIGIN::UNKNOWN;
860 filterSelectionItemPart(selectMps, mpBuild.FaberTypeRequired, itemOrigin);
862 else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
864 filterSelectionItemSpecific(selectMps, mpBuild.SpecificItemRequired);
866 else
868 nlstop;
871 // Sort before displaying
872 sortSelection(selectMps);
874 // Reset the DB selection
875 resetSelection();
876 fillSelection(selectMps);
878 // Bkup Selection for Validate later
879 _MpSelectionItemReqLine= itemReqLine;
880 _MpCurrentSelection= selectMps;
882 // Open the Selection Window.
883 CWidgetManager::getInstance()->enableModalWindow(ctrlSlot, FaberPhraseMpListModal);
887 // ***************************************************************************
888 void CActionPhraseFaber::validateMpSelection(uint selectId)
890 CInterfaceManager *pIM= CInterfaceManager::getInstance();
892 if(selectId>=_MpCurrentSelection.size())
894 CWidgetManager::getInstance()->disableModalWindow();
895 return;
898 // get which MP of the inventory we have selected
899 uint newInvSlot= _MpCurrentSelection[selectId];
901 // get the build
902 uint itemReqLine= _MpSelectionItemReqLine;
903 CMPBuild &mpBuild= _MPBuild[itemReqLine];
905 // Select the quantity to peek from this inventory slot: get max possible
906 sint quantity= mpBuild.QuantityReq - getTotalQuantitySetuped(itemReqLine);
907 nlassert(quantity>0);
908 quantity= min((sint32)quantity, _InventoryMirror[newInvSlot].Quantity);
910 // it may be possible (by update DB and error) that selected slot is no more usable => just quit
911 if(quantity<=0)
912 return;
914 // And Remove (virtually) item stack from this slot
915 _InventoryMirror[newInvSlot].Quantity-= quantity;
916 // mark as selected for this itemReqLine, so can no more select it
917 _InventoryMirror[newInvSlot].Selected|= 1<<itemReqLine;
919 // update the build
920 nlassert(mpBuild.NumMpSlot<MAX_MP_SLOT);
921 mpBuild.Id[mpBuild.NumMpSlot]= newInvSlot;
922 mpBuild.QuantitySelected[mpBuild.NumMpSlot]= quantity;
923 mpBuild.NumMpSlot++;
925 // Update The Execution View
926 CItem item= _InventoryMirror[newInvSlot];
927 item.Quantity= quantity;
928 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpBuild.NumMpSlot-1), item);
930 // update the empty slot
931 updateEmptySlot(itemReqLine);
933 // update quantity view
934 updateQuantityView(itemReqLine);
936 // update the validateButton
937 updateValidButton();
939 // update the item result
940 updateItemResult();
942 // must hide the modal window which had open us. NB: must be done here because next,
943 // we'll open the MP quantity selection
944 CWidgetManager::getInstance()->disableModalWindow();
946 // **** when all is correctly ended, open the quantity selection
947 // NB: just enable this code, if you want this feature
948 //startMpSelection(itemReqLine, mpBuild.NumMpSlot-1);
951 // ***************************************************************************
952 void CActionPhraseFaber::validateMpSelectQuantity()
954 CInterfaceManager *pIM= CInterfaceManager::getInstance();
956 // get current execution context of the validate
957 uint itemReqLine= _MpSelectionItemReqLine;
958 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
959 CMPBuild &mpBuild= _MPBuild[itemReqLine];
960 uint mpSlot= _MpChangeQuantitySlot;
961 nlassert(mpSlot<mpBuild.NumMpSlot);
962 uint invSlot= mpBuild.Id[mpSlot];
963 nlassert(invSlot<_InventoryMirror.size());
964 nlassert(_InventoryMirror[invSlot].Selected & (1<<itemReqLine));
966 // get the quantity selected
967 uint quantitySelected= 0;
968 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(MPQuantitySelectDb + ":CUR_QUANTITY", false);
969 if(node) quantitySelected= node->getValue32();
971 // maximize (if error)
972 quantitySelected= min(quantitySelected, getMaxQuantityChange(itemReqLine, mpSlot));
974 // if the new quantity is 0
975 if(quantitySelected==0)
977 // special: remove the mp slot from list
978 deleteMpSlot(itemReqLine, mpSlot);
980 else
982 // restore old quantity into inventory
983 _InventoryMirror[invSlot].Quantity+= mpBuild.QuantitySelected[mpSlot];
984 // And then Remove (virtually) new item stack from this slot
985 _InventoryMirror[invSlot].Quantity-= quantitySelected;
987 // update the build
988 mpBuild.QuantitySelected[mpSlot]= quantitySelected;
990 // Update The Execution View
991 CItem item= _InventoryMirror[invSlot];
992 item.Quantity= quantitySelected;
993 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpSlot), item);
996 // update the empty slot
997 updateEmptySlot(itemReqLine);
999 // update quantity view
1000 updateQuantityView(itemReqLine);
1002 // update the valid button
1003 updateValidButton();
1005 // update the item result
1006 updateItemResult();
1008 // hide the Modal Quantity selection
1009 CWidgetManager::getInstance()->disableModalWindow();
1012 // ***************************************************************************
1013 void CActionPhraseFaber::validateExecution()
1015 // the plan has must been selected
1016 nlassert(_ExecuteFromItemPlanBrick);
1018 // Build the list of MP in Bag.
1019 vector<CFaberMsgItem> mpItemPartList;
1020 vector<CFaberMsgItem> specificItemList;
1022 // Run all the current Build execution
1023 for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++)
1025 CMPBuild &mpBuild= _MPBuild[itemReqLine];
1026 // For all slot setuped.
1027 for(uint mpSlot=0;mpSlot<mpBuild.NumMpSlot;mpSlot++)
1029 CFaberMsgItem item;
1031 uint invSlot= mpBuild.Id[mpSlot];
1032 nlassert(invSlot<_InventoryMirror.size());
1033 item.setInvId(INVENTORIES::TInventory(_InventoryMirror[invSlot].InventoryId));
1034 item.IndexInInv= _InventoryMirror[invSlot].IdInInventory;
1035 item.Quantity= mpBuild.QuantitySelected[mpSlot];
1037 if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
1038 mpItemPartList.push_back(item);
1039 else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
1040 specificItemList.push_back(item);
1041 else
1043 nlstop;
1048 // display next craft action, and Send message to server
1049 CSPhraseManager *pPM= CSPhraseManager::getInstance();
1050 pPM->executeCraft(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex,
1051 _ExecuteFromItemPlanBrick->Id.asInt(), mpItemPartList, specificItemList);
1053 // Open the Interface to get the crafted item
1054 CTempInvManager::getInstance()->open(TEMP_INV_MODE::Craft);
1056 // NO more Close the Execution window (allow refaber quick)
1057 /*CInterfaceManager *pIM= CInterfaceManager::getInstance();
1058 CInterfaceElement *window= CWidgetManager::getInstance()->getElementFromId(FaberPhraseWindow);
1059 if(window)
1060 window->setActive(false);
1066 // ***************************************************************************
1067 uint CActionPhraseFaber::getTotalQuantitySetuped(uint itemReqLine) const
1069 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
1070 nlassert(_MPBuild[itemReqLine].NumMpSlot<=MAX_MP_SLOT);
1072 uint ret= 0;
1073 for(uint i=0;i<_MPBuild[itemReqLine].NumMpSlot;i++)
1075 ret+= _MPBuild[itemReqLine].QuantitySelected[i];
1078 return ret;
1082 // ***************************************************************************
1083 uint CActionPhraseFaber::getMaxQuantityChange(uint itemReqLine, uint mpSlot) const
1085 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
1086 nlassert(mpSlot<_MPBuild[itemReqLine].NumMpSlot);
1088 uint invSlot= _MPBuild[itemReqLine].Id[mpSlot];
1089 nlassert(invSlot<_InventoryMirror.size());
1090 CItem item= _InventoryMirror[invSlot];
1092 // This is the quantity already selected for this itemReqLine, + rest in inventory
1093 uint maxQuantity= _MPBuild[itemReqLine].QuantitySelected[mpSlot] + item.Quantity;
1094 // maximize with the rest of quantity with have to setup (remove us btw)
1095 maxQuantity= min(maxQuantity, _MPBuild[itemReqLine].QuantityReq -
1096 (getTotalQuantitySetuped(itemReqLine) - _MPBuild[itemReqLine].QuantitySelected[mpSlot]) );
1098 return maxQuantity;
1102 // ***************************************************************************
1103 void CActionPhraseFaber::updateEmptySlot(uint itemReqLine, CInterfaceGroup *itemReqLineGroup)
1105 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1107 if(!itemReqLineGroup)
1108 itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
1109 if(!itemReqLineGroup)
1110 return;
1112 // get the list sheet and ctrlButton.
1113 CDBGroupListSheet *listSheet= dynamic_cast<CDBGroupListSheet*>(itemReqLineGroup->getGroup(FaberPhraseList));
1114 if(!listSheet)
1115 return;
1117 // NB: forceValidity calls invalidateCoords() if state change => dont "clear then set".
1119 // All Setuped?
1120 bool allSetuped= getTotalQuantitySetuped(itemReqLine) >= _MPBuild[itemReqLine].QuantityReq;
1122 // button no more needed?
1123 if(allSetuped)
1125 // Reset all ForceValid
1126 for(uint i=0;i<MAX_MP_SLOT;i++)
1127 listSheet->forceValidity(i, false);
1129 else
1131 // Reset all ForceValid
1132 for(uint i=0;i<MAX_MP_SLOT;i++)
1134 listSheet->forceValidity(i, i==_MPBuild[itemReqLine].NumMpSlot);
1138 // Special for Specific Item requirement. Setup grayed item for the last empty slot
1139 for(uint i=0;i<MAX_MP_SLOT;i++)
1141 CMPBuild &mpBuild= _MPBuild[itemReqLine];
1143 // *** Fill the empty DB.
1144 if(i>=mpBuild.NumMpSlot)
1146 CItem item;
1147 // If Specfific requirement and just the last one, don't leave empty
1148 if(!allSetuped && i==mpBuild.NumMpSlot && mpBuild.RequirementType== CMPBuild::SpecificItemReq)
1150 item.Sheet= mpBuild.SpecificItemRequired;
1152 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, i), item);
1155 // *** Grayed,NoQuantity,NoQuality for the last slot of a specific requirement
1156 CDBCtrlSheet *ctrl= listSheet->getSheet(i);
1157 if(ctrl)
1159 if(i==mpBuild.NumMpSlot && mpBuild.RequirementType== CMPBuild::SpecificItemReq)
1161 ctrl->setUseQuality(false);
1162 ctrl->setUseQuantity(false);
1163 ctrl->setGrayed(true);
1165 else
1167 ctrl->setUseQuality(true);
1168 ctrl->setUseQuantity(true);
1169 ctrl->setGrayed(false);
1176 // ***************************************************************************
1177 void CActionPhraseFaber::updateQuantityView(uint itemReqLine)
1179 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
1181 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1183 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:SELECTED", MPQuantityDb.c_str(), itemReqLine), false);
1184 if(node)
1185 node->setValue32(getTotalQuantitySetuped(itemReqLine));
1186 node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:REQUIRED", MPQuantityDb.c_str(), itemReqLine), false);
1187 if(node)
1188 node->setValue32(_MPBuild[itemReqLine].QuantityReq);
1192 // ***************************************************************************
1193 void CActionPhraseFaber::updateValidButton()
1195 // Check For All MPSlot: If All Ok, then can validate!
1196 bool canValid= true;
1198 // can validate only if the Plan has been selected
1199 if(_ExecuteFromItemPlanBrick)
1201 // Run all the current Build execution
1202 for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++)
1204 canValid= canValid && getTotalQuantitySetuped(itemReqLine)==_MPBuild[itemReqLine].QuantityReq;
1207 else
1208 canValid= false;
1210 // unfreeze if valid
1211 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1212 CCtrlBaseButton *validButton= dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
1213 if(validButton) validButton->setFrozen(!canValid);
1217 // ***************************************************************************
1218 void CActionPhraseFaber::deleteMpSlot(uint itemReqLine, uint mpSlot)
1220 nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
1221 CMPBuild &mpBuild= _MPBuild[itemReqLine];
1222 nlassert(mpSlot<mpBuild.NumMpSlot);
1223 uint invSlot= mpBuild.Id[mpSlot];
1224 nlassert(invSlot<_InventoryMirror.size());
1225 nlassert(_InventoryMirror[invSlot].Selected & (1<<itemReqLine));
1226 // NB: possible that mpBuild.QuantitySelected[mpSlot]==0 (call from removeMpSlotThatUseInvSlot())
1228 // restore quantity into inventory
1229 _InventoryMirror[invSlot].Quantity+= mpBuild.QuantitySelected[mpSlot];
1230 // no more selected for this itemReqLine, so can select it now
1231 _InventoryMirror[invSlot].Selected&= ~(1<<itemReqLine);
1233 // update the build by shifting in memory
1234 uint i;
1235 for(i=mpSlot;i<mpBuild.NumMpSlot-1;i++)
1237 mpBuild.Id[i]= mpBuild.Id[i+1];
1238 mpBuild.QuantitySelected[i]= mpBuild.QuantitySelected[i+1];
1240 mpBuild.NumMpSlot--;
1242 // update the execution view (just what needed)
1243 for(i=mpSlot;i<mpBuild.NumMpSlot;i++)
1245 CItem item= _InventoryMirror[mpBuild.Id[i]];
1246 item.Quantity= mpBuild.QuantitySelected[i];
1247 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, i), item);
1250 // reset the empty slot!
1251 CItem item;
1252 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpBuild.NumMpSlot), item);
1257 // ***************************************************************************
1258 // ***************************************************************************
1259 // Handlers
1260 // ***************************************************************************
1261 // ***************************************************************************
1264 static CActionPhraseFaber *ActionPhraseFaber = NULL;
1267 // ***************************************************************************
1268 // This expr is used only for define in phrase.xml.
1269 DECLARE_INTERFACE_CONSTANT(getPhraseMPSelectionMax, MAX_MP_SELECTION_ENTRIES)
1272 // ***************************************************************************
1273 class CHandlerPhraseFaberSelectMP : public IActionHandler
1275 public:
1276 virtual void execute (CCtrlBase *pCaller, const string &Params)
1278 CDBCtrlSheet *ctrl= dynamic_cast<CDBCtrlSheet*>(pCaller);
1279 if(!ctrl)
1280 return;
1282 // get itemReqLine to Modify
1283 uint itemReqLine;
1284 fromString(getParam(Params, "item_req"), itemReqLine);
1285 // get mpSlot edited
1286 uint mpSlot= ctrl->getIndexInDB();
1288 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1289 ActionPhraseFaber->startMpSelection(itemReqLine, mpSlot);
1292 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberSelectMP, "phrase_faber_select_mp");
1295 // ***************************************************************************
1296 class CHandlerPhraseFaberValidateMP : public IActionHandler
1298 public:
1299 virtual void execute (CCtrlBase *pCaller, const string &/* Params */)
1301 CDBCtrlSheet *ctrl= dynamic_cast<CDBCtrlSheet*>(pCaller);
1302 if(!ctrl)
1304 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1305 CWidgetManager::getInstance()->disableModalWindow();
1306 return;
1309 // get the selected MP.
1310 uint selectMP= ctrl->getIndexInDB();
1312 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1313 ActionPhraseFaber->validateMpSelection(selectMP);
1316 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidateMP, "phrase_faber_validate_mp");
1319 // ***************************************************************************
1320 class CHandlerPhraseFaberValidate : public IActionHandler
1322 public:
1323 virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */)
1325 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1326 ActionPhraseFaber->validateExecution();
1329 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidate, "phrase_faber_validate");
1332 // ***************************************************************************
1333 class CHandlerPhraseFaberValidateOnEnter : public IActionHandler
1335 public:
1336 virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */)
1338 // get the button
1339 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1340 CCtrlBaseButton *button= dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
1342 // Ok, button found. test if active.
1343 if( button && !button->getFrozen() )
1345 // Act as if the player click on this button
1346 CAHManager::getInstance()->runActionHandler("phrase_faber_validate", button );
1350 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberValidateOnEnter, "phrase_faber_validate_on_enter");
1353 // ***************************************************************************
1354 class CHandlerPhraseFaberSelectMpQuantity : public IActionHandler
1356 public:
1357 virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */)
1359 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1360 ActionPhraseFaber->validateMpSelectQuantity();
1363 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberSelectMpQuantity, "phrase_faber_select_mp_quantity");
1367 // ***************************************************************************
1368 void launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick)
1370 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1371 ActionPhraseFaber->launchFaberCastWindow(memoryLine, memoryIndex, rootBrick);
1374 // ***************************************************************************
1375 void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType)
1377 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1378 ActionPhraseFaber->fillFaberPlanSelection(brickDB, maxSelection, toolType);
1381 // ***************************************************************************
1382 void validateFaberPlanSelection(CSBrickSheet *itemPlanBrick)
1384 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1385 ActionPhraseFaber->validateFaberPlanSelection(itemPlanBrick);
1388 // ***************************************************************************
1389 void closeFaberCastWindow()
1391 if (ActionPhraseFaber == NULL) return;
1392 CGroupContainer *window= dynamic_cast<CGroupContainer*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseWindow));
1393 if(window && window->getActive())
1394 window->setActive(false);
1398 // ***************************************************************************
1399 class CHandlerPhraseFaberOnClose : public IActionHandler
1401 public:
1402 virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */)
1404 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1405 ActionPhraseFaber->onCloseFaberCastWindow();
1408 REGISTER_ACTION_HANDLER( CHandlerPhraseFaberOnClose, "phrase_faber_on_close");
1411 // ***************************************************************************
1412 // ***************************************************************************
1413 // Management of Change in Inventory
1414 // ***************************************************************************
1415 // ***************************************************************************
1418 // ***************************************************************************
1419 void CActionPhraseFaber::removeMpSlotThatUseInvSlot(uint invSlot, uint quantityToRemove)
1421 if(quantityToRemove==0)
1422 return;
1424 // remove from all mpSlots
1425 for(uint itemReqLine=0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++)
1427 CMPBuild &mpBuild= _MPBuild[itemReqLine];
1428 for(uint mpSlot=0;mpSlot<mpBuild.NumMpSlot;)
1430 // if this mpSlot use the invSlot.
1431 if(mpBuild.Id[mpSlot]==invSlot)
1433 // then remove stack mp. If slot not enough
1434 uint removeSlotQuantity;
1435 removeSlotQuantity= min(quantityToRemove, mpBuild.QuantitySelected[mpSlot]);
1436 mpBuild.QuantitySelected[mpSlot]-= removeSlotQuantity;
1437 quantityToRemove-= removeSlotQuantity;
1439 // if slot completely removed, then remove it totaly
1440 if(mpBuild.QuantitySelected[mpSlot]==0)
1442 // NB: nothing is restored to inventory since mpBuild.QuantitySelected[mpSlot] is reseted before
1443 deleteMpSlot(itemReqLine, mpSlot);
1445 else
1447 // just change this DB view
1448 CItem item= _InventoryMirror[invSlot];
1449 item.Quantity= mpBuild.QuantitySelected[mpSlot];
1450 fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpSlot), item);
1452 // go to next slot
1453 mpSlot++;
1456 // if ok, all req quantity removed, quit
1457 if(quantityToRemove==0)
1458 return;
1460 else
1461 mpSlot++;
1467 // ***************************************************************************
1468 void CActionPhraseFaber::onInventoryChange()
1470 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1472 uint i;
1473 bool displayChange= false;
1475 // If the Faber Plan has not yet been selected, then must not check _InventoryMirror, since not initialized
1476 if(_ExecuteFromItemPlanBrick==NULL)
1477 return;
1479 // Run all the Bag
1480 uint invId = 0;
1481 uint indexInInv = 0;
1482 for(i=0;i<_InventoryMirror.size();i++)
1484 CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv);
1486 if(itemImage)
1488 CSheetId sheetId= CSheetId(itemImage->getSheetID());
1489 CItemSheet *mpSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(sheetId));
1490 CItem newInvItem;
1492 bool bLockedByOwner = itemImage->getLockedByOwner();
1494 // The item must be a mp, and the item must be available and unlocked
1495 if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner)
1497 newInvItem.Sheet= sheetId;
1498 newInvItem.Quality= itemImage->getQuality();
1499 newInvItem.Quantity= itemImage->getQuantity();
1500 newInvItem.UserColor= itemImage->getUserColor();
1501 newInvItem.Weight= itemImage->getWeight();
1502 newInvItem.OriginalQuantity= newInvItem.Quantity;
1503 newInvItem.LockedByOwner = bLockedByOwner;
1504 // remember these for sorting
1505 newInvItem.MpFamily = mpSheet->Mp.Family;
1506 newInvItem.Ecosystem = mpSheet->Mp.Ecosystem;
1507 newInvItem.StatEnergy = mpSheet->Mp.StatEnergy;
1510 /* There is 5 cases:
1511 - no changes => no op.
1512 - new/unlocked Mp on a empty or non Mp slot. Easy, just add.
1513 - old Mp removed (not same sheetId/quality/userColor/locked)
1514 - old Mp with quantity changed to be greater
1515 - old Mp with quantity changed to be smaller
1518 CItem &curInvItem= _InventoryMirror[i];
1520 // Bkup Id in newInvItem (for ope= correctness)
1521 newInvItem.InventoryId= curInvItem.InventoryId;
1522 newInvItem.IdInInventory= curInvItem.IdInInventory;
1524 // If the item was not a mp
1525 if(_InventoryMirror[i].Sheet==CSheetId::Unknown)
1527 // if now it is, easy, just add if not locked
1528 if(newInvItem.Sheet!=CSheetId::Unknown && !newInvItem.LockedByOwner)
1529 curInvItem= newInvItem;
1531 // else must test change or remove
1532 else
1534 bool sameMp;
1535 sameMp= curInvItem.Sheet == newInvItem.Sheet &&
1536 curInvItem.Quality == newInvItem.Quality &&
1537 curInvItem.UserColor == newInvItem.UserColor &&
1538 curInvItem.LockedByOwner == newInvItem.LockedByOwner;
1540 // if the Mp was deleted from this slot, delete it from all faber execution
1541 if(!sameMp)
1543 // remove all from current execution
1544 removeMpSlotThatUseInvSlot(i, curInvItem.OriginalQuantity);
1546 // replace (with nothing or new different Mp)
1547 curInvItem= newInvItem;
1549 // mpSlot may have been deleted
1550 displayChange= true;
1552 // test change of quantity
1553 else
1555 // if the quantity is the same, no op!
1556 if(curInvItem.OriginalQuantity!=newInvItem.OriginalQuantity)
1558 // if the quantity is now greater, its easy
1559 if(newInvItem.OriginalQuantity > curInvItem.OriginalQuantity)
1561 // just add the difference to the original and current setuped quantity
1562 uint32 diff= newInvItem.OriginalQuantity - curInvItem.OriginalQuantity;
1563 curInvItem.OriginalQuantity+= diff;
1564 curInvItem.Quantity+= diff;
1566 else
1568 // complex, must remove the quantity that has changed
1569 uint32 diff= curInvItem.OriginalQuantity - newInvItem.OriginalQuantity;
1570 // try first to remove it from remaining quantity
1571 if(curInvItem.Quantity>=(sint32)diff)
1573 // no change to current mpSlots!
1574 curInvItem.Quantity-= diff;
1576 // must close the selection modal if opened
1577 displayChange= true;
1579 // The remaining quantity is not enough, must also remove from mpSlot that use it!
1580 else
1582 uint32 toRemoveFromSlot= diff - curInvItem.Quantity;
1583 curInvItem.Quantity= 0;
1584 // remove all needed to current mp slot.
1585 removeMpSlotThatUseInvSlot(i, toRemoveFromSlot);
1587 // mpSlot may have been deleted
1588 displayChange= true;
1591 // bkup new original quantity
1592 curInvItem.OriginalQuantity= newInvItem.OriginalQuantity;
1600 // must update display?
1601 if(displayChange)
1603 for(uint itemReqLine= 0;itemReqLine<_MPBuildNumTotalItemReq;itemReqLine++)
1605 updateEmptySlot(itemReqLine);
1606 updateQuantityView(itemReqLine);
1608 updateValidButton();
1610 // close selection modals if they are opened
1611 CInterfaceGroup *quantityModal= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpQuantityModal));
1612 if(quantityModal && CWidgetManager::getInstance()->getModalWindow()==quantityModal)
1613 CWidgetManager::getInstance()->disableModalWindow();
1614 CInterfaceGroup *listModal= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpListModal));
1615 if(listModal && CWidgetManager::getInstance()->getModalWindow()==listModal)
1616 CWidgetManager::getInstance()->disableModalWindow();
1618 // update item result
1619 updateItemResult();
1623 // ***************************************************************************
1624 void CActionPhraseFaber::CDBInventoryObs::update(ICDBNode * /* node */)
1626 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1627 ActionPhraseFaber->onInventoryChange();
1630 // ***************************************************************************
1631 void CActionPhraseFaber::CDBAnimalObs::update(ICDBNode * /* node */)
1633 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
1634 ActionPhraseFaber->onInventoryChange();
1639 // ***************************************************************************
1640 void CActionPhraseFaber::updateItemResult()
1642 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1643 CSPhraseManager *pPM= CSPhraseManager::getInstance();
1644 CSBrickManager *pBM= CSBrickManager::getInstance();
1646 uint i;
1648 // level is the min level of MP
1649 sint32 minLevel= INT_MAX;
1650 // Stat is computed like server
1651 float statArray[RM_FABER_STAT_TYPE::NumRMStatType];
1652 float statCount[RM_FABER_STAT_TYPE::NumRMStatType];
1653 uint64 itemStatBF= 0;
1654 for(i=0;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
1656 statArray[i]= 0;
1657 statCount[i]= 0;
1659 // Color stat (for armour)
1660 uint32 bestItemColor[RM_COLOR::NumColors];
1661 for(i=0;i<RM_COLOR::NumColors;i++)
1662 bestItemColor[i]= 0;
1663 // Stat energy is computed like server
1664 float statEnergy= 0;
1667 // **** Parse all Bricks of the phrase executed, to get min level
1668 // take The brick with the lowest CR_RECOMMENDED
1669 uint phraseSlot= pPM->getMemorizedPhrase(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex);
1670 const CSPhraseCom &phrase= pPM->getPhrase(phraseSlot);
1671 uint32 recommendedPropId= pBM->getBrickPropId("cr_recommended");
1672 uint32 crHp = pBM->getBrickPropId("cr_hp");
1673 uint32 crSap = pBM->getBrickPropId("cr_sap");
1674 uint32 crSta = pBM->getBrickPropId("cr_sta");
1675 uint32 crFocus = pBM->getBrickPropId("cr_focus");
1676 sint32 hpBuff = 0;
1677 sint32 sapBuff = 0;
1678 sint32 staBuff = 0;
1679 sint32 focusBuff = 0;
1680 for(i=0;i<phrase.Bricks.size();i++)
1682 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
1683 if(brick)
1685 for(uint j=0;j<brick->Properties.size();j++)
1687 // if a CR_RECOMMENDED propId
1688 if(brick->Properties[j].PropId == recommendedPropId)
1690 // minimze the level
1691 minLevel= min(minLevel, sint32(brick->Properties[j].Value));
1694 if (brick->Properties[j].PropId == crHp)
1695 hpBuff = sint32(brick->Properties[j].Value);
1696 if (brick->Properties[j].PropId == crSap)
1697 sapBuff = sint32(brick->Properties[j].Value);
1698 if (brick->Properties[j].PropId == crSta)
1699 staBuff = sint32(brick->Properties[j].Value);
1700 if (brick->Properties[j].PropId == crFocus)
1701 focusBuff = sint32(brick->Properties[j].Value);
1707 NLGUI::CDBManager *cdb = NLGUI::CDBManager::getInstance();
1708 NLMISC::CCDBNodeLeaf *node = cdb->getDbProp(FaberPlanHpBuffDB, false);
1709 if (node) node->setValue32(hpBuff);
1710 node = cdb->getDbProp(FaberPlanSapBuffDB, false);
1711 if (node) node->setValue32(sapBuff);
1712 node = cdb->getDbProp(FaberPlanStaBuffDB, false);
1713 if (node) node->setValue32(staBuff);
1714 node = cdb->getDbProp(FaberPlanFocusBuffDB, false);
1715 if (node) node->setValue32(focusBuff);
1718 // **** Parse all MPs setuped, to compute level and stats
1719 uint totalItemPartMPReq= 0;
1720 uint totalItemPartMPSetuped= 0;
1721 for(i=0;i<_MPBuildNumTotalItemReq;i++)
1723 CMPBuild &mpBuild= _MPBuild[i];
1725 // --- ItemPart requirement?
1726 if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
1728 // For all slots setuped
1729 uint nSlot= min((uint)MAX_MP_SLOT, mpBuild.NumMpSlot);
1730 for(uint j=0;j<nSlot;j++)
1732 // Try to get the MP in this slot
1733 CItemSheet *mp= dynamic_cast<CItemSheet*>(SheetMngr.get(_InventoryMirror[mpBuild.Id[j]].Sheet));
1734 if(mp && mp->canBuildItemPart(mpBuild.FaberTypeRequired))
1736 // minimize level
1737 minLevel= min(_InventoryMirror[mpBuild.Id[j]].Quality, minLevel);
1739 // Increment stat for each of this MP selected
1740 const CItemSheet::CMpItemPart &mpIP= mp->getItemPart(mpBuild.FaberTypeRequired);
1742 // append to the stats
1743 uint numMps= mpBuild.QuantitySelected[j];
1744 for(uint k=0;k<RM_FABER_STAT_TYPE::NumRMStatType;k++)
1746 if(RM_FABER_STAT_TYPE::isStatRelevant(mpBuild.FaberTypeRequired, (RM_FABER_STAT_TYPE::TRMStatType)k))
1748 // %age to 0-1.
1749 statArray[k]+= numMps * (mpIP.Stats[k]/100.f);
1753 // Same for total energy
1754 statEnergy+= numMps * (mp->Mp.StatEnergy/100.f);
1756 // Increment color stat
1757 if(mp->Mp.MpColor>=0 && mp->Mp.MpColor<RM_COLOR::NumColors)
1759 bestItemColor[mp->Mp.MpColor]+= numMps;
1762 // Total MP setuped
1763 totalItemPartMPSetuped+= numMps;
1767 // get all stat for this item, and count MP req per stat
1768 for(uint k=0;k<RM_FABER_STAT_TYPE::NumRMStatType;k++)
1770 // if item part 'i' affect stat 'k'
1771 if(RM_FABER_STAT_TYPE::isStatRelevant(mpBuild.FaberTypeRequired, (RM_FABER_STAT_TYPE::TRMStatType)k))
1773 // StatPerItemPart ored in StatPerItem
1774 itemStatBF|= uint64(1)<<k;
1775 // Total Num MP per stat
1776 statCount[k]+= mpBuild.QuantityReq;
1780 // Total MP Req
1781 totalItemPartMPReq+= mpBuild.QuantityReq;
1784 // --- Specific Item requirement?
1785 else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
1787 // For all slots setuped
1788 uint nSlot= min((uint)MAX_MP_SLOT, mpBuild.NumMpSlot);
1789 for(uint j=0;j<nSlot;j++)
1791 // Try to get the MP in this slot
1792 CItemSheet *mp= dynamic_cast<CItemSheet*>(SheetMngr.get(_InventoryMirror[mpBuild.Id[j]].Sheet));
1793 if(mp->Id == mpBuild.SpecificItemRequired)
1795 // minimize level
1796 minLevel= min(_InventoryMirror[mpBuild.Id[j]].Quality, minLevel);
1798 // Formula 's Specific MPs don't impact on stats.
1804 // Mean stat
1805 for(i=0;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
1807 if(statCount[i])
1808 statArray[i]/= statCount[i];
1809 clamp(statArray[i], 0.f, 1.f);
1812 // Mean stat energy
1813 if(totalItemPartMPReq)
1815 statEnergy/= totalItemPartMPReq;
1816 clamp(statEnergy, 0.f, 1.f);
1819 // As in server, stretch the stats.
1820 // Add the special bonus ONLY when all MPs are setuped, for clearness
1821 RM_FABER_STAT_TYPE::stretchItemStats(statArray, itemStatBF, totalItemPartMPSetuped>=totalItemPartMPReq);
1824 // **** setup Level
1825 if(minLevel==INT_MAX)
1826 minLevel= 0;
1827 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetLevel, false);
1828 if(node)
1829 node->setValue32(minLevel);
1832 // **** change success rate too
1833 CViewText *successView= dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseFpSuccessText));
1834 if(successView)
1836 string text= CI18N::get("uiPhraseFaberSuccessRate");
1837 // Get the success rate of the related phrase
1838 uint phraseSlot= pPM->getMemorizedPhrase(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex);
1840 sint32 craftSuccessModifier = 0;
1841 CCDBNodeLeaf *nodeCSM= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:CRAFT", false);
1842 if(nodeCSM)
1844 craftSuccessModifier = nodeCSM->getValue32();
1846 // With the faber plan skill
1847 sint success= pPM->getCraftPhraseSuccessRate(pPM->getPhrase(phraseSlot), _ExecuteFromItemPlanBrick->getSkill(), minLevel, 0);
1848 sint bonus = pPM->getCraftPhraseSuccessRate(pPM->getPhrase(phraseSlot), _ExecuteFromItemPlanBrick->getSkill(), minLevel, (sint) craftSuccessModifier) - success;
1849 string successStr;
1850 if( bonus == 0 )
1852 successStr = toString("@{FFFF}") + toString(success);
1854 else
1855 if( bonus > 0 ) // bonus
1857 successStr = "@{0F0F}" + toString(success + bonus)
1858 + "@{FFFF}("
1859 + toString( success )
1860 + "@{0F0F} + "
1861 + toString( bonus )
1862 + "@{FFFF})";
1864 else
1866 successStr = "@{E42F}" + toString(success + bonus)
1867 + "@{FFFF}("
1868 + toString( success )
1869 + "@{E42F} - "
1870 + toString( abs(bonus) )
1871 + "@{FFFF})";
1873 strFindReplace(text, "%success", successStr );
1874 successView->setTextFormatTaged(text);
1878 // **** setup Color
1879 // Same than server code (NB: beige==1 per default)
1880 uint maxNumColor = 0;
1881 uint dominanteColor = 1;
1882 for(i = 0; i < RM_COLOR::NumColors; ++i )
1884 if( bestItemColor[i] > maxNumColor )
1886 maxNumColor = bestItemColor[i];
1887 dominanteColor = i;
1890 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetColor, false);
1891 if(node)
1892 node->setValue32(dominanteColor);
1895 // **** Get Stat validity
1896 uint64 itemStatFinalUsageBF= 0;
1897 // Some stat (magic protection and magic resist) are finaly used in the item only for the best ones
1898 itemStatFinalUsageBF= RM_FABER_STAT_TYPE::getStatFinalValidity(statArray, itemStatBF);
1901 // **** Stats
1902 CInterfaceGroup *groupMp= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseItemResultGroup));
1903 if(groupMp)
1905 // default: hide all
1906 for(i=0;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
1908 // get the stat group
1909 CInterfaceGroup *groupStat= dynamic_cast<CInterfaceGroup*>(groupMp->getElement(groupMp->getId()+toString(":stat%d",i) ));
1910 if(groupStat)
1911 groupStat->setActive(false);
1913 // enable only one that are relevant for this item
1914 uint groupIndex= 0;
1915 for(i=0;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
1917 RM_FABER_STAT_TYPE::TRMStatType statType= RM_FABER_STAT_TYPE::TRMStatType(i);
1919 // if this stat is not relevant for the item, don't display it!
1920 if( (itemStatBF&(uint64(1)<<i)) == 0)
1921 continue;
1923 // Is the stat finaly used? (magic protection for instance may not be)
1924 bool finalyUsed= (itemStatFinalUsageBF&(uint64(1)<<i)) != 0;
1925 CRGBA usageColor= finalyUsed?(CRGBA::White):CRGBA(128,128,128);
1927 // get the next stat group
1928 CInterfaceGroup *groupStat= dynamic_cast<CInterfaceGroup*>(groupMp->getElement(groupMp->getId()+toString(":stat%d",groupIndex) ));
1929 if(groupStat)
1931 groupStat->setActive(true);
1932 // fill text and bar according to stat
1933 CViewText *statTitle= dynamic_cast<CViewText*>(groupStat->getElement(groupStat->getId()+":text" ));
1934 CDBViewBar *statValueBar= dynamic_cast<CDBViewBar*>(groupStat->getElement(groupStat->getId()+":bar" ));
1935 CViewText *statValueText= dynamic_cast<CViewText*>(groupStat->getElement(groupStat->getId()+":textstat" ));
1936 CCtrlBase *statToolTip= dynamic_cast<CCtrlBase*>(groupStat->getElement(groupStat->getId()+":tt" ));
1937 uint sv= uint(statArray[i]*100);
1938 if(statTitle)
1940 statTitle->setText(RM_FABER_STAT_TYPE::toLocalString(statType));
1941 statTitle->setColor(usageColor);
1943 if(statValueBar)
1945 statValueBar->setValue(sv);
1946 statValueBar->setColor(usageColor);
1948 if(statValueText)
1950 statValueText->setText(toString(sv)+"/100");
1951 statValueText->setColor(usageColor);
1953 if(statToolTip)
1955 if(finalyUsed)
1957 // display something only for magic/protect stat
1958 if( RM_FABER_STAT_TYPE::isMagicResistStat(RM_FABER_STAT_TYPE::TRMStatType(i)) ||
1959 RM_FABER_STAT_TYPE::isMagicProtectStat(RM_FABER_STAT_TYPE::TRMStatType(i)) )
1960 statToolTip->setDefaultContextHelp(CI18N::get("uiFaberStatActive"));
1961 else
1962 statToolTip->setDefaultContextHelp(std::string());
1964 else
1965 statToolTip->setDefaultContextHelp(CI18N::get("uiFaberStatGrayed"));
1969 groupIndex++;
1974 // **** BestStat (for text over)
1975 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetStatType, false);
1976 if(node)
1978 float bestStatValue =-1.0f;
1979 RM_FABER_STAT_TYPE::TRMStatType bestStat = RM_FABER_STAT_TYPE::NumRMStatType;
1980 for( i = 0; i < RM_FABER_STAT_TYPE::NumRMStatType; ++i )
1982 // if this stat is not relevant for the item, don't use it!
1983 if( (itemStatBF&(uint64(1)<<i)) == 0)
1984 continue;
1985 float value = statArray[i];
1986 if( value > bestStatValue )
1988 bestStatValue = value;
1989 bestStat = (RM_FABER_STAT_TYPE::TRMStatType)i;
1992 // Setup DB
1993 node->setValue32(bestStat);
1997 // **** ClassType (for text over)
1998 node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetClassType, false);
1999 if(node)
2001 // Setup DB
2002 node->setValue32(RM_CLASS_TYPE::getItemClass((uint32)(100.0f * statEnergy)));
2007 /* Handle change of skill -> recompute success rate */
2008 void CActionPhraseFaber::CSkillObserver::onSkillChange()
2010 if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber;
2011 // Dont update if the plan has not yet been selected
2012 if(ActionPhraseFaber->_ExecuteFromItemPlanBrick==NULL)
2013 return;
2014 ActionPhraseFaber->updateItemResult();
2017 void CActionPhraseFaber::CDBModCraftObs::update(ICDBNode * /* node */)
2019 if (ActionPhraseFaber == NULL || ActionPhraseFaber->_ExecuteFromItemPlanBrick == NULL)
2020 return;
2022 ActionPhraseFaber->updateItemResult();