// 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 "nel/misc/algo.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= rootBrick->Properties[0].Text;
strupr(prop);
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();
}