khanat-opennel-code/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp
2013-02-14 09:53:06 -06:00

1859 lines
61 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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 "nel/gui/action_handler.h"
#include "../client_cfg.h"
#include "nel/gui/ctrl_base_button.h"
#include "nel/gui/group_container.h"
#include "../string_manager_client.h"
#include "../net_manager.h"
#include "sbrick_manager.h"
#include "sphrase_manager.h"
#include "nel/gui/group_editbox.h"
#include "nel/gui/dbview_bar.h"
#include "skill_manager.h"
#include "game_share/bot_chat_types.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()
{
uint size = MAX_PLAYER_INV_ENTRIES + (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL) +
MAX_GUILDINV_ENTRIES + MAX_ROOMINV_ENTRIES;
_InventoryMirror.resize(size);
_InventoryObsSetup= false;
_ExecuteFromItemPlanBrick= NULL;
}
// ***************************************************************************
void CActionPhraseFaber::fillDBWithMP(const std::string &sheetBase, const CItem &item)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":SHEET")->setValue32(item.Sheet.asInt());
NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":QUALITY")->setValue32(item.Quality);
NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":QUANTITY")->setValue32(item.Quantity);
NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":USER_COLOR")->setValue32(item.UserColor);
NLGUI::CDBManager::getInstance()->getDbProp(sheetBase + ":WEIGHT")->setValue32(item.Weight);
}
// ***************************************************************************
void CActionPhraseFaber::launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick)
{
// **** Get the ItemSheet for faber plan. NULL => no op.
if(!rootBrick)
return;
// Copy Execution launch
_ExecuteFromMemoryLine= memoryLine;
_ExecuteFromMemoryIndex= memoryIndex;
// no item plan setuped for now
_ExecuteFromItemPlanBrick= NULL;
// get the family of item plan (for selection) from the rootBrick. It is stored in the Property0.
_FaberPlanBrickFamilies.clear();
if(rootBrick->Properties.size()>0)
{
string prop= NLMISC::toUpper(rootBrick->Properties[0].Text);
vector<string> strList;
splitString(prop, " ", strList);
// The prop Id should be 'FPLAN:'
if(strList.size()>=2 && strList[0]=="FPLAN:")
{
for(uint i=1;i<strList.size();i++)
{
BRICK_FAMILIES::TBrickFamily bfam= BRICK_FAMILIES::toSBrickFamily(strList[i]);
if(bfam!=BRICK_FAMILIES::Unknown)
_FaberPlanBrickFamilies.push_back(bfam);
}
}
}
// if not found, error, cannot choose the faber plan
if(_FaberPlanBrickFamilies.empty())
{
nlwarning("ERROR: The Craft Root %s does not contain a valid FPLAN: property -> 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<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
if(validButton)
validButton->setFrozen(true);
// reset DB, hide the Mps
uint itemReqLine;
for(itemReqLine=0;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
{
// Reset All Mps slots.
for(uint mpSlot=0;mpSlot<MAX_MP_SLOT;mpSlot++)
{
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
if(itemReqLineGroup)
itemReqLineGroup->setActive(false);
}
// Reset the selected plan
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanDB, false);
if(node)
node->setValue32(0);
// Reset the result item
node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetDB, false);
if(node)
node->setValue32(0);
// Hide the ItemResult group
CInterfaceGroup *groupMp= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseItemResultGroup));
if(groupMp)
groupMp->setActive(false);
// **** Open the window!
CGroupContainer *window= dynamic_cast<CGroupContainer*>(CWidgetManager::getInstance()->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= NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:INVENTORY:BAG");
if(branch) NLGUI::CDBManager::getInstance()->removeBranchObserver( "LOCAL:INVENTORY:BAG",&_DBInventoryObs);
if(branch) NLGUI::CDBManager::getInstance()->addBranchObserver( "LOCAL:INVENTORY:BAG",&_DBInventoryObs);
// and for all pack animals
uint i;
for(i=0;i<MAX_INVENTORY_ANIMAL;i++)
{
branch= NLGUI::CDBManager::getInstance()->getDbBranch(toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i));
if(branch) NLGUI::CDBManager::getInstance()->removeBranchObserver( toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i).c_str(), &_DBInventoryObs);
if(branch) NLGUI::CDBManager::getInstance()->addBranchObserver( toString("LOCAL:INVENTORY:PACK_ANIMAL%d", i).c_str(), &_DBInventoryObs);
}
// Add observers on animal status, cause inventory may become unavailabe during the process
for(i=0;i<MAX_INVENTORY_ANIMAL;i++)
{
node= NLGUI::CDBManager::getInstance()->getDbProp(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= NLGUI::CDBManager::getInstance()->getDbBranch("LOCAL:INVENTORY:BAG");
if(branch) branch->removeBranchObserver(&_DBInventoryObs);
// and for all pack animals
for(uint i=0;i<MAX_INVENTORY_ANIMAL;i++)
{
branch= NLGUI::CDBManager::getInstance()->getDbBranch(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;i<MAX_INVENTORY_ANIMAL;i++)
{
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(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<CSheetId> bricks;
for(i=0;i<_FaberPlanBrickFamilies.size();i++)
{
const std::vector<NLMISC::CSheetId> &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;i<maxSelection;i++)
{
if(i<num)
NLGUI::CDBManager::getInstance()->getDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(bricks[i].asInt());
else
NLGUI::CDBManager::getInstance()->getDbProp(brickDB + ":" + toString(i) + ":SHEET")->setValue32(0);
}
}
// ***************************************************************************
CItemImage *CActionPhraseFaber::getInvMirrorItemImage(uint slotIndex, uint& invId, uint& indexInInv)
{
if (slotIndex < MAX_PLAYER_INV_ENTRIES)
{
invId = INVENTORIES::bag;
indexInInv = slotIndex;
return &getInventory().getBagItem(slotIndex);
}
slotIndex -= MAX_PLAYER_INV_ENTRIES;
if (slotIndex < (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL))
{
uint animal = slotIndex / MAX_ANIMALINV_ENTRIES;
uint index = slotIndex % MAX_ANIMALINV_ENTRIES;
invId = INVENTORIES::pet_animal + animal;
indexInInv = index;
return &getInventory().getPAItem(animal, index);
}
slotIndex -= (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL);
if (slotIndex < MAX_GUILDINV_ENTRIES)
{
if (getInventory().isInventoryAvailable(INVENTORIES::guild))
{
CInterfaceManager *im = CInterfaceManager::getInstance();
CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch(SERVER_INVENTORY ":GUILD:" + toString(slotIndex));
static CItemImage image;
image.build(itemBranch);
invId = INVENTORIES::guild;
indexInInv = slotIndex;
return &image;
}
return NULL;
}
slotIndex -= MAX_GUILDINV_ENTRIES;
if (slotIndex < MAX_ROOMINV_ENTRIES)
{
if (getInventory().isInventoryAvailable(INVENTORIES::player_room))
{
CInterfaceManager *im = CInterfaceManager::getInstance();
CCDBNodeBranch *itemBranch = NLGUI::CDBManager::getInstance()->getDbBranch(SERVER_INVENTORY ":ROOM:" + toString(slotIndex));
static CItemImage image;
image.build(itemBranch);
invId = INVENTORIES::player_room;
indexInInv = slotIndex;
return &image;
}
return NULL;
}
return NULL;
}
// ***************************************************************************
bool CActionPhraseFaber::isMpAvailable(CItemSheet *mpSheet, uint invId, uint slotIndex) const
{
return mpSheet && mpSheet->Family==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= NLGUI::CDBManager::getInstance()->getDbProp(FaberPlanDB, false);
if(node)
node->setValue32(_ExecuteFromItemPlanBrick->Id.asInt());
// Setup the result item
node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetDB, false);
if(node)
node->setValue32(itemPlanBrick->FaberPlan.ItemBuilt.asInt());
// Setup the result quantity (for stacked items)
node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultQuantityDB, false);
if(node)
node->setValue32(itemPlanBrick->FaberPlan.NbItemBuilt);
// Show the ItemResult group
CInterfaceGroup *groupMp= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->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;mpSlot<MAX_MP_SLOT;mpSlot++)
{
mpBuild.Id[mpSlot]= 0; // useless, but for consistency
mpBuild.QuantitySelected[mpSlot]= 0;
}
}
// reset other to 0 also
for(;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
{
_MPBuild[itemReqLine].reset();
}
// **** First clear and copy the inventory to local struct
uint i;
for(i=0;i<_InventoryMirror.size();i++)
{
_InventoryMirror[i].reset();
}
uint invId = 0;
uint indexInInv = 0;
// Run all the inventories.
for(i=0;i<_InventoryMirror.size();i++)
{
CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv);
bool bLockedByOwner = itemImage && itemImage->getLockedByOwner();
// item found and not locked?
if(itemImage)
{
// setup the origin
_InventoryMirror[i].InventoryId= invId;
_InventoryMirror[i].IdInInventory= indexInInv;
// The item must be a mp
CSheetId sheetId= CSheetId(itemImage->getSheetID());
CItemSheet *mpSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(sheetId));
if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner)
{
_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;
_InventoryMirror[i].LockedByOwner= bLockedByOwner;
}
}
}
// **** show ItemParts according to plan.
// Hide the valid button
CCtrlBaseButton *validButton= dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
if(validButton)
validButton->setFrozen(true);
// reset DB, show/hide the Mps
for(itemReqLine=0;itemReqLine<MAX_ITEM_REQ_LINE;itemReqLine++)
{
CMPBuild &mpBuild= _MPBuild[itemReqLine];
// Reset All Mps slots.
for(uint mpSlot=0;mpSlot<MAX_MP_SLOT;mpSlot++)
{
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:%d:SHEET", MPFaberDB.c_str(), itemReqLine, mpSlot), false);
if(node)
node->setValue32(0);
}
// Setup item requirement groups
CInterfaceGroup *itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->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<CViewText*>(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<CViewBitmap*>(itemReqLineGroup->getView(FaberPhraseIcon));
if(viewBmp)
{
if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
{
// texture name in config.xml
viewBmp->setTexture(CWidgetManager::getInstance()->getParser()->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<const CItemSheet*>(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<CGroupContainer*>(CWidgetManager::getInstance()->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;i<MAX_MP_SELECTION_ENTRIES;i++)
{
NLGUI::CDBManager::getInstance()->getDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0);
}
}
// ***************************************************************************
void CActionPhraseFaber::fillSelection(const std::vector<uint> &mps)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
uint num= min(uint(MAX_MP_SELECTION_ENTRIES), uint(mps.size()));
for(uint i=0;i<MAX_MP_SELECTION_ENTRIES;i++)
{
if(i<num && mps[i]<_InventoryMirror.size())
{
CItem &item= _InventoryMirror[mps[i]];
fillDBWithMP(MPSelectionDB+ ":" + toString(i), item);
}
else
NLGUI::CDBManager::getInstance()->getDbProp(MPSelectionDB+ ":" + toString(i) + ":SHEET")->setValue32(0);
}
}
// ***************************************************************************
void CActionPhraseFaber::filterSelectionItemPart(std::vector<uint> &mps, RM_FABER_TYPE::TRMFType itemPartFilter, ITEM_ORIGIN::EItemOrigin originFilter)
{
// Unknown => no fitler
if(itemPartFilter==RM_FABER_TYPE::Unknown)
return;
std::vector<uint> res;
res.reserve(mps.size());
for(uint i=0;i<mps.size();i++)
{
// get the item sheet
const CItemSheet *itemSheet= dynamic_cast<const CItemSheet*>(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<uint> &mps, NLMISC::CSheetId specificItemWanted)
{
std::vector<uint> res;
res.reserve(mps.size());
// if unknown sheetid, no match
if(specificItemWanted==NLMISC::CSheetId::Unknown)
{
mps.clear();
return;
}
for(uint i=0;i<mps.size();i++)
{
// get the item sheet
const CItemSheet *itemSheet= dynamic_cast<const CItemSheet*>(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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
if(itemReqLineGroup)
{
CDBGroupListSheet *listSheet= dynamic_cast<CDBGroupListSheet*>(itemReqLineGroup->getGroup(FaberPhraseList));
if(listSheet)
ctrlSlot= listSheet->getSheet(mpSlot);
}
if(!ctrlSlot)
return;
// get the mpBuild setup
nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
CMPBuild &mpBuild= _MPBuild[itemReqLine];
// If the slot selected is already filled, Launch the MP Quantity selection modal
if(mpSlot<mpBuild.NumMpSlot)
{
// fill the sheet info
uint invSlot= mpBuild.Id[mpSlot];
CItem item= _InventoryMirror[invSlot];
fillDBWithMP(MPQuantitySelectDb, item);
// compute the maximum quantity possible to fill
uint maxQuantity= getMaxQuantityChange(itemReqLine, mpSlot);
// set the max quantity as the default quantity to set up.
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(MPQuantitySelectDb + ":CUR_QUANTITY", false);
if(node) node->setValue32(maxQuantity);
node= NLGUI::CDBManager::getInstance()->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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpQuantityModal));
if(quantityModal)
{
CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(quantityModal->getGroup("eb"));
if (eb)
{
CWidgetManager::getInstance()->setCaptureKeyboard(eb);
eb->setInputString(toString(maxQuantity));
eb->setSelectionAll();
}
}
// launch the modal
CWidgetManager::getInstance()->enableModalWindow(ctrlSlot, quantityModal);
}
// else select new MP
else
{
// For All the inventory
vector<uint> 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<<itemReqLine))==0 )
{
selectMps.push_back(i);
}
}
// Filter the selection whether it is an itemPart or specificItem reqiurement
if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
{
CItemSheet *itemBuilt= dynamic_cast<CItemSheet*>(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.
CWidgetManager::getInstance()->enableModalWindow(ctrlSlot, FaberPhraseMpListModal);
}
}
// ***************************************************************************
void CActionPhraseFaber::validateMpSelection(uint selectId)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
if(selectId>=_MpCurrentSelection.size())
{
CWidgetManager::getInstance()->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<<itemReqLine;
// update the build
nlassert(mpBuild.NumMpSlot<MAX_MP_SLOT);
mpBuild.Id[mpBuild.NumMpSlot]= newInvSlot;
mpBuild.QuantitySelected[mpBuild.NumMpSlot]= quantity;
mpBuild.NumMpSlot++;
// Update The Execution View
CItem item= _InventoryMirror[newInvSlot];
item.Quantity= quantity;
fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpBuild.NumMpSlot-1), item);
// update the empty slot
updateEmptySlot(itemReqLine);
// update quantity view
updateQuantityView(itemReqLine);
// update the validateButton
updateValidButton();
// update the item result
updateItemResult();
// must hide the modal window which had open us. NB: must be done here because next,
// we'll open the MP quantity selection
CWidgetManager::getInstance()->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(itemReqLine<MAX_ITEM_REQ_LINE);
CMPBuild &mpBuild= _MPBuild[itemReqLine];
uint mpSlot= _MpChangeQuantitySlot;
nlassert(mpSlot<mpBuild.NumMpSlot);
uint invSlot= mpBuild.Id[mpSlot];
nlassert(invSlot<_InventoryMirror.size());
nlassert(_InventoryMirror[invSlot].Selected & (1<<itemReqLine));
// get the quantity selected
uint quantitySelected= 0;
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(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
CWidgetManager::getInstance()->disableModalWindow();
}
// ***************************************************************************
void CActionPhraseFaber::validateExecution()
{
// the plan has must been selected
nlassert(_ExecuteFromItemPlanBrick);
// Build the list of MP in Bag.
vector<CFaberMsgItem> mpItemPartList;
vector<CFaberMsgItem> 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;mpSlot<mpBuild.NumMpSlot;mpSlot++)
{
CFaberMsgItem item;
uint invSlot= mpBuild.Id[mpSlot];
nlassert(invSlot<_InventoryMirror.size());
item.setInvId(INVENTORIES::TInventory(_InventoryMirror[invSlot].InventoryId));
item.IndexInInv= _InventoryMirror[invSlot].IdInInventory;
item.Quantity= mpBuild.QuantitySelected[mpSlot];
if(mpBuild.RequirementType==CMPBuild::ItemPartReq)
mpItemPartList.push_back(item);
else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
specificItemList.push_back(item);
else
{
nlstop;
}
}
}
// display next craft action, and Send message to server
CSPhraseManager *pPM= CSPhraseManager::getInstance();
pPM->executeCraft(_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= CWidgetManager::getInstance()->getElementFromId(FaberPhraseWindow);
if(window)
window->setActive(false);
*/
}
// ***************************************************************************
uint CActionPhraseFaber::getTotalQuantitySetuped(uint itemReqLine) const
{
nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
nlassert(_MPBuild[itemReqLine].NumMpSlot<=MAX_MP_SLOT);
uint ret= 0;
for(uint i=0;i<_MPBuild[itemReqLine].NumMpSlot;i++)
{
ret+= _MPBuild[itemReqLine].QuantitySelected[i];
}
return ret;
}
// ***************************************************************************
uint CActionPhraseFaber::getMaxQuantityChange(uint itemReqLine, uint mpSlot) const
{
nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
nlassert(mpSlot<_MPBuild[itemReqLine].NumMpSlot);
uint invSlot= _MPBuild[itemReqLine].Id[mpSlot];
nlassert(invSlot<_InventoryMirror.size());
CItem item= _InventoryMirror[invSlot];
// This is the quantity already selected for this itemReqLine, + rest in inventory
uint maxQuantity= _MPBuild[itemReqLine].QuantitySelected[mpSlot] + item.Quantity;
// maximize with the rest of quantity with have to setup (remove us btw)
maxQuantity= min(maxQuantity, _MPBuild[itemReqLine].QuantityReq -
(getTotalQuantitySetuped(itemReqLine) - _MPBuild[itemReqLine].QuantitySelected[mpSlot]) );
return maxQuantity;
}
// ***************************************************************************
void CActionPhraseFaber::updateEmptySlot(uint itemReqLine, CInterfaceGroup *itemReqLineGroup)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
if(!itemReqLineGroup)
itemReqLineGroup= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId( toString(FaberPhraseItemReqLine.c_str(), itemReqLine) ));
if(!itemReqLineGroup)
return;
// get the list sheet and ctrlButton.
CDBGroupListSheet *listSheet= dynamic_cast<CDBGroupListSheet*>(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;i<MAX_MP_SLOT;i++)
listSheet->forceValidity(i, false);
}
else
{
// Reset all ForceValid
for(uint i=0;i<MAX_MP_SLOT;i++)
{
listSheet->forceValidity(i, i==_MPBuild[itemReqLine].NumMpSlot);
}
}
// Special for Specific Item requirement. Setup grayed item for the last empty slot
for(uint i=0;i<MAX_MP_SLOT;i++)
{
CMPBuild &mpBuild= _MPBuild[itemReqLine];
// *** Fill the empty DB.
if(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(itemReqLine<MAX_ITEM_REQ_LINE);
CInterfaceManager *pIM= CInterfaceManager::getInstance();
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("%s:%d:SELECTED", MPQuantityDb.c_str(), itemReqLine), false);
if(node)
node->setValue32(getTotalQuantitySetuped(itemReqLine));
node= NLGUI::CDBManager::getInstance()->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<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
if(validButton) validButton->setFrozen(!canValid);
}
// ***************************************************************************
void CActionPhraseFaber::deleteMpSlot(uint itemReqLine, uint mpSlot)
{
nlassert(itemReqLine<MAX_ITEM_REQ_LINE);
CMPBuild &mpBuild= _MPBuild[itemReqLine];
nlassert(mpSlot<mpBuild.NumMpSlot);
uint invSlot= mpBuild.Id[mpSlot];
nlassert(invSlot<_InventoryMirror.size());
nlassert(_InventoryMirror[invSlot].Selected & (1<<itemReqLine));
// NB: possible that mpBuild.QuantitySelected[mpSlot]==0 (call from removeMpSlotThatUseInvSlot())
// restore quantity into inventory
_InventoryMirror[invSlot].Quantity+= mpBuild.QuantitySelected[mpSlot];
// no more selected for this itemReqLine, so can select it now
_InventoryMirror[invSlot].Selected&= ~(1<<itemReqLine);
// update the build by shifting in memory
uint i;
for(i=mpSlot;i<mpBuild.NumMpSlot-1;i++)
{
mpBuild.Id[i]= mpBuild.Id[i+1];
mpBuild.QuantitySelected[i]= mpBuild.QuantitySelected[i+1];
}
mpBuild.NumMpSlot--;
// update the execution view (just what needed)
for(i=mpSlot;i<mpBuild.NumMpSlot;i++)
{
CItem item= _InventoryMirror[mpBuild.Id[i]];
item.Quantity= mpBuild.QuantitySelected[i];
fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, i), item);
}
// reset the empty slot!
CItem item;
fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpBuild.NumMpSlot), item);
}
// ***************************************************************************
// ***************************************************************************
// Handlers
// ***************************************************************************
// ***************************************************************************
static CActionPhraseFaber *ActionPhraseFaber = NULL;
// ***************************************************************************
// This expr is used only for define in phrase.xml.
DECLARE_INTERFACE_CONSTANT(getPhraseMPSelectionMax, MAX_MP_SELECTION_ENTRIES)
// ***************************************************************************
class CHandlerPhraseFaberSelectMP : public IActionHandler
{
public:
virtual void execute (CCtrlBase *pCaller, const string &Params)
{
CDBCtrlSheet *ctrl= dynamic_cast<CDBCtrlSheet*>(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<CDBCtrlSheet*>(pCaller);
if(!ctrl)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
CWidgetManager::getInstance()->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<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseValidButton));
// Ok, button found. test if active.
if( button && !button->getFrozen() )
{
// Act as if the player click on this button
CAHManager::getInstance()->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<CGroupContainer*>(CWidgetManager::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;mpSlot<mpBuild.NumMpSlot;)
{
// if this mpSlot use the invSlot.
if(mpBuild.Id[mpSlot]==invSlot)
{
// then remove stack mp. If slot not enough
uint removeSlotQuantity;
removeSlotQuantity= min(quantityToRemove, mpBuild.QuantitySelected[mpSlot]);
mpBuild.QuantitySelected[mpSlot]-= removeSlotQuantity;
quantityToRemove-= removeSlotQuantity;
// if slot completely removed, then remove it totaly
if(mpBuild.QuantitySelected[mpSlot]==0)
{
// NB: nothing is restored to inventory since mpBuild.QuantitySelected[mpSlot] is reseted before
deleteMpSlot(itemReqLine, mpSlot);
}
else
{
// just change this DB view
CItem item= _InventoryMirror[invSlot];
item.Quantity= mpBuild.QuantitySelected[mpSlot];
fillDBWithMP(toString("%s:%d:%d", MPFaberDB.c_str(), itemReqLine, mpSlot), item);
// go to next slot
mpSlot++;
}
// if ok, all req quantity removed, quit
if(quantityToRemove==0)
return;
}
else
mpSlot++;
}
}
}
// ***************************************************************************
void CActionPhraseFaber::onInventoryChange()
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
uint i;
bool displayChange= false;
// If the Faber Plan has not yet been selected, then must not check _InventoryMirror, since not initialized
if(_ExecuteFromItemPlanBrick==NULL)
return;
// Run all the Bag
uint invId = 0;
uint indexInInv = 0;
for(i=0;i<_InventoryMirror.size();i++)
{
CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv);
if(itemImage)
{
CSheetId sheetId= CSheetId(itemImage->getSheetID());
CItemSheet *mpSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(sheetId));
CItem newInvItem;
bool bLockedByOwner = itemImage->getLockedByOwner();
// The item must be a mp, and the item must be available and unlocked
if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner)
{
newInvItem.Sheet= sheetId;
newInvItem.Quality= itemImage->getQuality();
newInvItem.Quantity= itemImage->getQuantity();
newInvItem.UserColor= itemImage->getUserColor();
newInvItem.Weight= itemImage->getWeight();
newInvItem.OriginalQuantity= newInvItem.Quantity;
newInvItem.LockedByOwner = bLockedByOwner;
}
/* There is 5 cases:
- no changes => no op.
- new/unlocked Mp on a empty or non Mp slot. Easy, just add.
- old Mp removed (not same sheetId/quality/userColor/locked)
- 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 not locked
if(newInvItem.Sheet!=CSheetId::Unknown && !newInvItem.LockedByOwner)
curInvItem= newInvItem;
}
// else must test change or remove
else
{
bool sameMp;
sameMp= curInvItem.Sheet == newInvItem.Sheet &&
curInvItem.Quality == newInvItem.Quality &&
curInvItem.UserColor == newInvItem.UserColor &&
curInvItem.LockedByOwner == newInvItem.LockedByOwner;
// 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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpQuantityModal));
if(quantityModal && CWidgetManager::getInstance()->getModalWindow()==quantityModal)
CWidgetManager::getInstance()->disableModalWindow();
CInterfaceGroup *listModal= dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseMpListModal));
if(listModal && CWidgetManager::getInstance()->getModalWindow()==listModal)
CWidgetManager::getInstance()->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;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
{
statArray[i]= 0;
statCount[i]= 0;
}
// Color stat (for armour)
uint32 bestItemColor[RM_COLOR::NumColors];
for(i=0;i<RM_COLOR::NumColors;i++)
bestItemColor[i]= 0;
// Stat energy is computed like server
float statEnergy= 0;
// **** Parse all Bricks of the phrase executed, to get min level
// take The brick with the lowest CR_RECOMMENDED
uint phraseSlot= pPM->getMemorizedPhrase(_ExecuteFromMemoryLine, _ExecuteFromMemoryIndex);
const CSPhraseCom &phrase= pPM->getPhrase(phraseSlot);
uint32 recommendedPropId= pBM->getBrickPropId("cr_recommended");
for(i=0;i<phrase.Bricks.size();i++)
{
CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
if(brick)
{
for(uint j=0;j<brick->Properties.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<nSlot;j++)
{
// Try to get the MP in this slot
CItemSheet *mp= dynamic_cast<CItemSheet*>(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;k<RM_FABER_STAT_TYPE::NumRMStatType;k++)
{
if(RM_FABER_STAT_TYPE::isStatRelevant(mpBuild.FaberTypeRequired, (RM_FABER_STAT_TYPE::TRMStatType)k))
{
// %age to 0-1.
statArray[k]+= numMps * (mpIP.Stats[k]/100.f);
}
}
// Same for total energy
statEnergy+= numMps * (mp->Mp.StatEnergy/100.f);
// Increment color stat
if(mp->Mp.MpColor>=0 && mp->Mp.MpColor<RM_COLOR::NumColors)
{
bestItemColor[mp->Mp.MpColor]+= numMps;
}
// Total MP setuped
totalItemPartMPSetuped+= numMps;
}
}
// get all stat for this item, and count MP req per stat
for(uint k=0;k<RM_FABER_STAT_TYPE::NumRMStatType;k++)
{
// if item part 'i' affect stat 'k'
if(RM_FABER_STAT_TYPE::isStatRelevant(mpBuild.FaberTypeRequired, (RM_FABER_STAT_TYPE::TRMStatType)k))
{
// StatPerItemPart ored in StatPerItem
itemStatBF|= uint64(1)<<k;
// Total Num MP per stat
statCount[k]+= mpBuild.QuantityReq;
}
}
// Total MP Req
totalItemPartMPReq+= mpBuild.QuantityReq;
}
// --- Specific Item requirement?
else if(mpBuild.RequirementType==CMPBuild::SpecificItemReq)
{
// For all slots setuped
uint nSlot= min((uint)MAX_MP_SLOT, mpBuild.NumMpSlot);
for(uint j=0;j<nSlot;j++)
{
// Try to get the MP in this slot
CItemSheet *mp= dynamic_cast<CItemSheet*>(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<RM_FABER_STAT_TYPE::NumRMStatType;i++)
{
if(statCount[i])
statArray[i]/= statCount[i];
clamp(statArray[i], 0.f, 1.f);
}
// Mean stat energy
if(totalItemPartMPReq)
{
statEnergy/= totalItemPartMPReq;
clamp(statEnergy, 0.f, 1.f);
}
// As in server, stretch the stats.
// Add the special bonus ONLY when all MPs are setuped, for clearness
RM_FABER_STAT_TYPE::stretchItemStats(statArray, itemStatBF, totalItemPartMPSetuped>=totalItemPartMPReq);
// **** setup Level
if(minLevel==INT_MAX)
minLevel= 0;
CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(ItemResultSheetLevel, false);
if(node)
node->setValue32(minLevel);
// **** change success rate too
CViewText *successView= dynamic_cast<CViewText*>(CWidgetManager::getInstance()->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= NLGUI::CDBManager::getInstance()->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= NLGUI::CDBManager::getInstance()->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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(FaberPhraseItemResultGroup));
if(groupMp)
{
// default: hide all
for(i=0;i<RM_FABER_STAT_TYPE::NumRMStatType;i++)
{
// get the stat group
CInterfaceGroup *groupStat= dynamic_cast<CInterfaceGroup*>(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<RM_FABER_STAT_TYPE::NumRMStatType;i++)
{
RM_FABER_STAT_TYPE::TRMStatType statType= RM_FABER_STAT_TYPE::TRMStatType(i);
// if this stat is not relevant for the item, don't display it!
if( (itemStatBF&(uint64(1)<<i)) == 0)
continue;
// Is the stat finaly used? (magic protection for instance may not be)
bool finalyUsed= (itemStatFinalUsageBF&(uint64(1)<<i)) != 0;
CRGBA usageColor= finalyUsed?(CRGBA::White):CRGBA(128,128,128);
// get the next stat group
CInterfaceGroup *groupStat= dynamic_cast<CInterfaceGroup*>(groupMp->getElement(groupMp->getId()+toString(":stat%d",groupIndex) ));
if(groupStat)
{
groupStat->setActive(true);
// fill text and bar according to stat
CViewText *statTitle= dynamic_cast<CViewText*>(groupStat->getElement(groupStat->getId()+":text" ));
CDBViewBar *statValueBar= dynamic_cast<CDBViewBar*>(groupStat->getElement(groupStat->getId()+":bar" ));
CViewText *statValueText= dynamic_cast<CViewText*>(groupStat->getElement(groupStat->getId()+":textstat" ));
CCtrlBase *statToolTip= dynamic_cast<CCtrlBase*>(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= NLGUI::CDBManager::getInstance()->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)<<i)) == 0)
continue;
float value = statArray[i];
if( value > bestStatValue )
{
bestStatValue = value;
bestStat = (RM_FABER_STAT_TYPE::TRMStatType)i;
}
}
// Setup DB
node->setValue32(bestStat);
}
// **** ClassType (for text over)
node= NLGUI::CDBManager::getInstance()->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();
}