Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / interface_v3 / sphrase_manager.cpp
blob4ba54229db5bb11ed1b98bc6ac281d34f5b1dd56
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "stdpch.h"
24 #include "sphrase_manager.h"
25 #include "interface_manager.h"
26 #include "../client_sheets/sphrase_sheet.h"
27 #include "../sheet_manager.h"
28 #include "../string_manager_client.h"
29 #include "sbrick_manager.h"
30 #include "skill_manager.h"
31 #include "inventory_manager.h"
32 #include "../user_entity.h"
33 #include "game_share/memorization_set_types.h"
34 #include "../client_cfg.h"
35 #include "../net_manager.h"
36 #include "nel/misc/algo.h"
37 #include "../client_sheets/success_table_sheet.h"
38 #include "dbctrl_sheet.h"
39 #include "macrocmd_manager.h"
40 #include "game_share/rolemaster_flags.h"
41 #include "game_share/people.h"
42 #include "nel/gui/lua_ihm.h"
43 #include "../time_client.h"
46 using namespace std;
47 using namespace NLMISC;
48 using namespace NLGEORGES;
50 // Context help
51 extern void contextHelp (const std::string &help);
54 // ***************************************************************************
55 // define this for deubg purpose only
56 //#define PHRASE_DEBUG_VERBOSE
59 // ***************************************************************************
60 const std::string PhraseMemoryViewNextAction= "ui:interface:gestionsets:shortcuts:view_next_action";
61 const std::string PhraseMemoryViewCycleAction= "ui:interface:gestionsets:shortcuts:view_cycle_action";
62 const std::string PhraseMemoryViewSlotBase= "ui:interface:gestionsets:shortcuts:s";
63 const std::string PhraseMemoryCtrlBase= "ui:interface:gestionsets:shortcuts:s";
64 const std::string PhraseMemoryAltCtrlBase= "ui:interface:gestionsets2:header_closed:shortcuts:s";
66 const std::string PhraseMemoryPhraseMenu= "ui:interface:cm_memory_phrase";
67 const std::string PhraseMemoryPhraseAction= "cast_phrase_or_create_new";
68 const std::string PhraseMemoryMacroMenu= "ui:interface:cm_memory_macro";
69 const std::string PhraseMemoryMacroAction= "cast_macro";
72 // ***************************************************************************
73 CSPhraseManager *CSPhraseManager::_Instance= NULL;
75 // ***************************************************************************
76 SKILLS::ESkills getRightHandItemSkill();
77 uint32 getRightHandEffectiveLevel();
79 // ***************************************************************************
80 void CSPhraseManager::releaseInstance()
82 if( _Instance )
84 delete _Instance;
85 _Instance = NULL;
89 // ***************************************************************************
90 CSPhraseManager::CSPhraseManager()
92 reset();
93 for(uint i=0;i<NumSuccessTable;i++)
94 _SuccessTableSheet[i]= NULL;
95 _RegenTickRangeTouched = true;
98 // ***************************************************************************
99 CSPhraseManager::~CSPhraseManager()
101 reset();
104 // ***************************************************************************
105 void CSPhraseManager::initInGame()
107 if(_InitInGameDone)
108 return;
110 static bool registerClassDone= false;
113 _InitInGameDone= true;
115 // Init Database values.
116 CInterfaceManager *pIM= CInterfaceManager::getInstance();
117 uint i;
118 _BookDbLeaves.resize(PHRASE_MAX_BOOK_SLOT, NULL);
119 for(i=0;i<PHRASE_MAX_BOOK_SLOT;i++)
121 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_BOOK + ":" + toString(i) + ":PHRASE");
122 node->setValue32(0);
123 _BookDbLeaves[i]= node;
125 _MemoryDbLeaves.resize(PHRASE_MAX_MEMORY_SLOT, NULL);
126 _MemoryAltDbLeaves.resize(PHRASE_MAX_MEMORY_SLOT, NULL);
128 for(i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
130 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_MEMORY + ":" + toString(i) + ":PHRASE");
131 node->setValue32(0);
132 _MemoryDbLeaves[i]= node;
133 CCDBNodeLeaf *node_alt= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_MEMORY_ALT + ":" + toString(i) + ":PHRASE");
134 node_alt->setValue32(0);
135 _MemoryAltDbLeaves[i]= node_alt;
138 // Progression Db leaves
139 nlctassert( NumProgressType == sizeof(PHRASE_DB_PROGRESSION)/sizeof(PHRASE_DB_PROGRESSION[0]) );
140 for(uint j=0;j<NumProgressType;j++)
142 _ProgressionDbSheets[j].resize(PHRASE_MAX_PROGRESSION_SLOT, NULL);
143 _ProgressionDbLocks[j].resize(PHRASE_MAX_PROGRESSION_SLOT, NULL);
144 _ProgressionDbLevels[j].resize(PHRASE_MAX_PROGRESSION_SLOT, NULL);
146 for(i=0;i<PHRASE_MAX_PROGRESSION_SLOT;i++)
148 for(uint j=0;j<NumProgressType;j++)
150 // SHEET
151 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION[j] + ":" + toString(i) + ":SHEET");
152 node->setValue32(0);
153 _ProgressionDbSheets[j][i]= node;
154 // LEVEL
155 node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION[j] + ":" + toString(i) + ":LEVEL");
156 node->setValue32(0);
157 _ProgressionDbLevels[j][i]= node;
158 // LOCKED
159 node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION[j] + ":" + toString(i) + ":LOCKED");
160 node->setValue32(0);
161 _ProgressionDbLocks[j][i]= node;
165 // init the UI Next Execute slot
167 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_EXECUTE_NEXT);
168 node->setValue32(0);
169 _NextExecuteLeaf= node;
170 node= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_EXECUTE_NEXT_IS_CYCLIC);
171 node->setValue32(0);
172 _NextExecuteIsCyclicLeaf= node;
174 // Init BotChat leaves
175 _BotChatPhraseSheetLeaves.resize(PHRASE_MAX_BOTCHAT_SLOT, NULL);
176 _BotChatPhrasePriceLeaves.resize(PHRASE_MAX_BOTCHAT_SLOT, NULL);
177 for(i=0;i<PHRASE_MAX_BOTCHAT_SLOT;i++)
179 CCDBNodeLeaf *nodeSheet= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_BOTCHAT+ ":" + toString(i) + ":SHEET");
180 CCDBNodeLeaf *nodePrice= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_BOTCHAT+ ":" + toString(i) + ":PRICE");
181 _BotChatPhraseSheetLeaves[i]= nodeSheet;
182 _BotChatPhrasePriceLeaves[i]= nodePrice;
185 // and so update book and memory db
186 updateBookDB();
187 updateMemoryDBAll();
189 // Load the success table here
190 loadSuccessTable();
192 // compute and the progression phrase, and update DB
193 computePhraseProgression();
195 _EnchantWeaponMainBrick = NLMISC::CSheetId("bsxea10.sbrick");
197 // build map that gives its description for each built-in phrase
198 // slow test on all sheets here ...
199 const CSheetManager::TEntitySheetMap &sm = SheetMngr.getSheets();
200 sint32 result= 0;
201 CSPhraseCom tmpPhrase;
202 for(CSheetManager::TEntitySheetMap::const_iterator it = sm.begin(); it != sm.end(); ++it)
204 if (it->second.EntitySheet && it->second.EntitySheet->Type == CEntitySheet::SPHRASE)
206 const_cast<CSPhraseManager *>(this)->buildPhraseFromSheet(tmpPhrase, it->first.asInt());
207 _PhraseToSheet[tmpPhrase] = it->first.asInt();
213 // ***************************************************************************
214 void CSPhraseManager::updateMemoryBar()
216 if(!_InitInGameDone)
217 return;
219 updateMemoryDBAll();
220 updateAllMemoryCtrlState();
224 // ***************************************************************************
225 void CSPhraseManager::setPhraseInternal(uint32 slot, const CSPhraseCom &phrase, bool lock, bool updateDB)
227 // don't allow slot too big. don't allow set the 0 slot.
228 if(slot>PHRASE_MAX_ID || slot==0)
229 return;
231 // enlargePhraseClient
232 if(slot>=_PhraseClient.size())
233 _PhraseClient.resize(slot+1);
235 // set the phrase
236 _PhraseMap[slot]= phrase;
237 // increment the phrase version.
238 _PhraseClient[slot].Version++;
239 // BotChat lock?
240 _PhraseClient[slot].Lock= lock;
242 // For Free Slot Mgt.
243 _MaxSlotSet= max(_MaxSlotSet, slot);
245 // update the book, if necessary
246 if( updateDB && !lock && slot >= BookStartSlot )
247 updateBookDB();
250 // ***************************************************************************
251 void CSPhraseManager::setPhrase(uint32 slot, const CSPhraseCom &phrase, bool lock)
253 // set the phrase and update the DB
254 setPhraseInternal(slot, phrase, lock, true);
257 // ***************************************************************************
258 void CSPhraseManager::setPhraseNoUpdateDB(uint32 slot, const CSPhraseCom &phrase)
260 // set the phrase but don't update the DB (NB: no phrase lock)
261 setPhraseInternal(slot, phrase, false, false);
265 // ***************************************************************************
266 void CSPhraseManager::erasePhrase(uint32 slot)
268 if(slot>=_PhraseClient.size())
269 return;
271 _PhraseMap.erase(slot);
272 _PhraseClient[slot].Version= -1;
273 _PhraseClient[slot].Lock= false;
275 // make this slot available for allocation
276 _FreeSlots.push_back(slot);
278 // update the book.
279 updateBookDB();
281 // if the phrase erased was currently executed, must stop it
282 if(slot==_CurrentExecutePhraseIdCycle || slot==_CurrentExecutePhraseIdNext)
284 if(slot==_CurrentExecutePhraseIdNext)
286 _CurrentExecuteLineNext= -1;
287 _CurrentExecuteSlotNext= -1;
288 _CurrentExecutePhraseIdNext= 0;
290 if(slot==_CurrentExecutePhraseIdCycle)
292 _CurrentExecuteLineCycle= -1;
293 _CurrentExecuteSlotCycle= -1;
294 _CurrentExecutePhraseIdCycle= 0;
297 // update execution display
298 updateExecutionDisplay();
303 // ***************************************************************************
304 const CSPhraseCom &CSPhraseManager::getPhrase(uint32 slot) const
306 TPhraseMap::const_iterator it= _PhraseMap.find(slot);
307 if(it==_PhraseMap.end())
308 return CSPhraseCom::EmptyPhrase;
309 else
310 return it->second;
313 // ***************************************************************************
314 sint32 CSPhraseManager::getPhraseVersion(uint32 slot) const
316 if(slot>=_PhraseClient.size())
317 return -1;
319 return _PhraseClient[slot].Version;
322 // ***************************************************************************
323 void CSPhraseManager::unlockPhrase(uint32 slot)
325 TPhraseMap::const_iterator it= _PhraseMap.find(slot);
326 if(it!=_PhraseMap.end())
328 // unlcok the phrase
329 _PhraseClient[slot].Lock= false;
330 // and so update the book.
331 updateBookDB();
336 // ***************************************************************************
337 void CSPhraseManager::receiveBotChatConfirmBuy(uint16 phraseId, bool confirm)
339 // if confirm => unlock
340 if(confirm)
342 // first unlock it.
343 unlockPhrase(phraseId);
345 // Then try to auto memorize it.
346 const CSPhraseCom &phrase= getPhrase(phraseId);
347 if(!phrase.empty())
349 CSBrickManager *pBM= CSBrickManager::getInstance();
350 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
351 if(rootBrick)
353 // Must save to the first Memory slot available
354 for(uint dstMemoryLine=0; dstMemoryLine<MEM_SET_TYPES::NumMemories; dstMemoryLine++)
356 // Find a free slot to write.
357 sint dstMemorySlot= -1;
358 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
360 // if slot is free
361 if( !isMemorizedMacro(dstMemoryLine, i) &&
362 getMemorizedPhrase(dstMemoryLine, i)==0 )
364 dstMemorySlot= i;
365 break;
369 // If free slot found in this memory line
370 if(dstMemorySlot>=0)
372 // memorize
373 memorizePhrase(dstMemoryLine, dstMemorySlot, phraseId);
375 // Send Server Info
376 sendMemorizeToServer(dstMemoryLine, dstMemorySlot, phraseId);
378 // stop!
379 break;
383 // Ugly: must update ALL the memoryBar in case of dstMemoryLine is the selected one
384 updateAllMemoryCtrlState();
388 // Context help
389 contextHelp ("action_book");
391 // else delete slot
392 else
393 erasePhrase(phraseId);
397 // ***************************************************************************
398 void CSPhraseManager::forgetPhrase(uint32 memoryLine, uint32 memorySlot)
400 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
401 return;
402 // if the slot eihter don't exist, abort
403 if(memoryLine>=_Memories.size())
404 return;
405 // if the slot is not a phrase
406 if(!_Memories[memoryLine].Slot[memorySlot].isPhrase())
407 return;
409 // update memory
410 _Memories[memoryLine].Slot[memorySlot].IsMacro= false;
411 _Memories[memoryLine].Slot[memorySlot].Id= 0;
413 // must update DB?
414 if((sint32)memoryLine==_SelectedMemoryDB)
416 // update the db
417 updateMemoryDBSlot(memorySlot);
418 // update the ctrl state
419 updateMemoryCtrlState(memorySlot);
421 // If there is an execution running on this slot, no more display it
422 if(_CurrentExecuteSlotNext==(sint32)memorySlot || _CurrentExecuteSlotCycle==(sint32)memorySlot)
423 updateExecutionDisplay();
427 // ***************************************************************************
428 uint32 CSPhraseManager::getMemorizedPhrase(uint32 memoryLine, uint32 memorySlot) const
430 if(memoryLine>=_Memories.size())
431 return 0;
432 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
433 return 0;
434 if(!_Memories[memoryLine].Slot[memorySlot].isPhrase())
435 return 0;
437 return _Memories[memoryLine].Slot[memorySlot].Id;
440 // ***************************************************************************
441 void CSPhraseManager::memorizePhrase(uint32 memoryLine, uint32 memorySlot, uint32 slot)
443 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
444 return;
445 if(slot>=_PhraseClient.size())
446 return;
447 // Can memorize only a phrase of the book
448 if(slot<BookStartSlot)
449 return;
451 // first force forget old mem slot
452 forgetPhrase(memoryLine, memorySlot);
454 // then memorize new one.
455 TPhraseMap::const_iterator it= _PhraseMap.find(slot);
456 if(it==_PhraseMap.end())
457 return;
459 // enlarge memory if needed
460 if(memoryLine>=_Memories.size())
461 _Memories.resize(memoryLine+1);
463 // update memory
464 _Memories[memoryLine].Slot[memorySlot].IsMacro= false;
465 _Memories[memoryLine].Slot[memorySlot].Id= slot;
467 // must update DB?
468 if((sint32)memoryLine==_SelectedMemoryDB)
470 // update the DB
471 updateMemoryDBSlot(memorySlot);
472 // update the ctrl state
473 updateMemoryCtrlState(memorySlot);
475 // If there is an execution running with this action, maybe re-display it
476 if(_CurrentExecutePhraseIdNext==slot || _CurrentExecutePhraseIdCycle==slot)
477 updateExecutionDisplay();
481 // ***************************************************************************
482 void CSPhraseManager::selectMemoryLineDB(sint32 memoryLine)
484 if(memoryLine<0)
485 memoryLine= -1;
487 if(_SelectedMemoryDB!=memoryLine)
489 _SelectedMemoryDB= memoryLine;
490 // since memory selection changes then must update all the DB and the Ctrl states
491 updateMemoryDBAll();
492 updateAllMemoryCtrlState();
493 updateAllMemoryCtrlRegenTickRange();
494 // must update also the execution views
495 updateExecutionDisplay();
499 void CSPhraseManager::selectMemoryLineDBalt(sint32 memoryLine)
501 if(memoryLine<0)
502 memoryLine= -1;
504 if(_SelectedMemoryDBalt!=memoryLine)
506 _SelectedMemoryDBalt= memoryLine;
507 // since memory selection changes then must update all the DB and the Ctrl states
508 updateMemoryDBAll();
509 updateAllMemoryCtrlState();
510 updateAllMemoryCtrlRegenTickRange();
511 // must update also the execution views
512 updateExecutionDisplay();
516 // ***************************************************************************
517 void CSPhraseManager::updateMemoryDBAll()
519 // If DB not inited, no-op
520 if(!_InitInGameDone)
521 return;
523 if(_SelectedMemoryDB==-1 || _SelectedMemoryDB>=(sint32)_Memories.size())
525 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
527 _MemoryDbLeaves[i]->setValue32(0);
530 else
532 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
534 CMemorySlot &slot= _Memories[_SelectedMemoryDB].Slot[i];
535 if(!slot.isPhrase())
536 _MemoryDbLeaves[i]->setValue32(0);
537 else
538 _MemoryDbLeaves[i]->setValue32(slot.Id);
542 if(_SelectedMemoryDBalt == -1 || _SelectedMemoryDBalt>=(sint32)_Memories.size())
544 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
546 _MemoryAltDbLeaves[i]->setValue32(0);
549 else
551 // Always update alt gestionsets
552 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
554 CMemorySlot &slotAlt= _Memories[_SelectedMemoryDBalt].Slot[i];
555 if(!slotAlt.isPhrase())
556 _MemoryAltDbLeaves[i]->setValue32(0);
557 else
558 _MemoryAltDbLeaves[i]->setValue32(slotAlt.Id);
563 // ***************************************************************************
564 void CSPhraseManager::updateMemoryDBSlot(uint32 memorySlot)
566 // If DB not inited, no-op
567 if(!_InitInGameDone)
568 return;
570 if(_SelectedMemoryDB==-1 || _SelectedMemoryDB>=(sint32)_Memories.size())
571 return;
573 if(memorySlot<PHRASE_MAX_MEMORY_SLOT)
575 CMemorySlot &slot= _Memories[_SelectedMemoryDB].Slot[memorySlot];
576 if(!slot.isPhrase())
577 _MemoryDbLeaves[memorySlot]->setValue32(0);
578 else
579 _MemoryDbLeaves[memorySlot]->setValue32(slot.Id);
581 CMemorySlot &slotAlt= _Memories[_SelectedMemoryDBalt].Slot[memorySlot];
583 if(!slotAlt.isPhrase())
584 _MemoryAltDbLeaves[memorySlot]->setValue32(0);
585 else
586 _MemoryAltDbLeaves[memorySlot]->setValue32(slotAlt.Id);
590 // ***************************************************************************
591 bool CSPhraseManager::isPhraseCastable(uint32 sheetId) const
593 return isPhraseCastable(dynamic_cast<CSPhraseSheet*>(SheetMngr.get(NLMISC::CSheetId(sheetId))));
596 // ***************************************************************************
597 bool CSPhraseManager::isPhraseCastable(CSPhraseSheet *phraseSheet) const
599 bool ok = false;
600 if(phraseSheet)
602 // castable
603 if(phraseSheet->Castable)
605 // check Charac Buying (compatibility)
606 if(!isPhraseCharacBuying(phraseSheet))
607 ok= true;
611 return ok;
614 // ***************************************************************************
615 bool CSPhraseManager::isPhraseCharacBuying(uint32 sheetId) const
617 return isPhraseCharacBuying(dynamic_cast<CSPhraseSheet*>(SheetMngr.get(NLMISC::CSheetId(sheetId))));
620 // ***************************************************************************
621 bool CSPhraseManager::isPhraseCharacBuying(class CSPhraseSheet *phraseSheet) const
623 CSBrickManager *pBM= CSBrickManager::getInstance();
624 if(!phraseSheet || phraseSheet->Bricks.empty())
625 return false;
627 // check brick
628 CSBrickSheet *brickSheet= pBM->getBrick(phraseSheet->Bricks[0]);
629 // if not a charac buying phrase
630 if(brickSheet && BRICK_FAMILIES::isCharacBuyFamily(brickSheet->BrickFamily) )
631 return true;
633 return false;
636 // ***************************************************************************
637 uint32 CSPhraseManager::allocatePhraseSlot()
639 if(_FreeSlots.empty())
641 // if too big, fail.
642 if(_MaxSlotSet>=PHRASE_MAX_ID)
643 return 0;
645 // get a free slot
646 return _MaxSlotSet+1;
648 else
650 uint32 val= _FreeSlots.back();
651 _FreeSlots.pop_back();
652 return val;
656 // ***************************************************************************
657 bool CSPhraseManager::hasFreeSlot() const
659 // if no free slot and too big, fail.
660 if(_FreeSlots.empty())
662 if(_MaxSlotSet>=PHRASE_MAX_ID)
663 return false;
666 return true;
669 // ***************************************************************************
670 void CSPhraseManager::setBookFilter(BRICK_TYPE::EBrickType brickTypeFilter, SKILLS::ESkills skillFilter)
672 // set the filter
673 _BookBrickTypeFilter = brickTypeFilter;
674 _BookSkillFitler = skillFilter;
676 // the DB book may change
677 updateBookDB();
678 // the DB progression may change
679 updatePhraseProgressionDB();
682 // ***************************************************************************
683 bool CSPhraseManager::matchBookSkillFilter(const CSPhraseCom &phrase) const
685 // Special: if book Filter is unknown, then always match!
686 if(_BookBrickTypeFilter==BRICK_TYPE::UNKNOWN && _BookSkillFitler==SKILLS::unknown)
687 return true;
689 CSBrickManager *pBM= CSBrickManager::getInstance();
690 CSkillManager *pSM= CSkillManager::getInstance();
692 // must have a valid rootBrick. NB: it is an error, but decide to show it (return true)
693 if(phrase.empty())
694 return true;
695 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
696 if(!rootBrick)
697 return true;
699 // If decide to filter by brickType
700 if(_BookBrickTypeFilter!=BRICK_TYPE::UNKNOWN)
702 if( BRICK_FAMILIES::brickType(rootBrick->BrickFamily) == _BookBrickTypeFilter)
703 return true;
705 // else filter by Skill
706 else
708 // Special for powers: always true. \todo yoyo: may change this feature
709 if(rootBrick->isSpecialPower())
711 // Skill Filter must be at least combat or magic
712 return pSM->areSkillOnSameBranch(_BookSkillFitler, SKILLS::SM) ||
713 pSM->areSkillOnSameBranch(_BookSkillFitler, SKILLS::SF);
715 // Special for combat
716 else if(rootBrick->isCombat())
718 // For combat, it is best to show "All Actions that can be used with the weapon associated to the skill"
719 if(skillCompatibleWithCombatPhrase(_BookSkillFitler, phrase.Bricks))
720 return true;
722 // suppose same for magic, craft, and forage
723 else
725 // run all the phrase Bricks, if only one match (eg important for magic multi spells), it is ok!
726 for(uint i=0;i<phrase.Bricks.size();i++)
728 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
729 if(brick)
731 // if the filter is an ancestor, ok! (NB: if brick skill unknown then it will fail!)
732 if(pSM->isSkillAncestor(_BookSkillFitler, brick->getSkill()))
733 return true;
739 // no match!
740 return false;
743 // ***************************************************************************
744 void CSPhraseManager::updateBookDB()
746 // If DB not inited, no-op
747 if(!_InitInGameDone)
748 return;
750 // Fill All the book.
751 TPhraseMap::const_iterator it= _PhraseMap.begin();
752 sint numBookFill= (sint)_PhraseMap.size();
753 sint i= 0;
754 while(i<numBookFill)
756 // if the slot is not from the book, then don't display it
757 // if the slot is Locked (BotChat confirm wait), then don't display it too
758 // if the slot does not match the filter, then don't display it too
759 if(it->first<BookStartSlot ||
760 _PhraseClient[it->first].Lock ||
761 !matchBookSkillFilter(it->second) )
763 numBookFill--;
765 else
767 // fill with the phrase id
768 _BookDbLeaves[i]->setValue32(it->first);
769 // next mem fill
770 i++;
773 // if no more place on book, stop
774 if(i>=PHRASE_MAX_BOOK_SLOT)
776 numBookFill= PHRASE_MAX_BOOK_SLOT;
777 break;
780 // next map entry.
781 it++;
784 // reset old no more used to empty
785 for(i=numBookFill;i<(sint)_LastBookNumDbFill;i++)
787 _BookDbLeaves[i]->setValue32(0);
790 // update cache
791 _LastBookNumDbFill= numBookFill;
794 // ***************************************************************************
795 void CSPhraseManager::buildPhraseFromSheet(CSPhraseCom &phrase, sint32 sheetId)
797 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(sheetId)));
798 if(phraseSheet)
800 // get localized Name
801 phrase.Name= STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedName(CSheetId(sheetId));
802 // Build bricks
803 phrase.Bricks.clear();
804 for(uint i=0;i<phraseSheet->Bricks.size();i++)
805 phrase.Bricks.push_back(phraseSheet->Bricks[i]);
807 else
809 phrase= CSPhraseCom::EmptyPhrase;
813 // ***************************************************************************
814 bool CSPhraseManager::isPhraseNextExecuteCounterSync() const
816 CInterfaceManager *pIM= CInterfaceManager::getInstance();
817 sint32 srvVal= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_NEXT)->getValue32();
818 return srvVal==(sint32)_PhraseNextExecuteCounter;
821 // ***************************************************************************
822 bool CSPhraseManager::isPhraseCycleExecuteCounterSync() const
824 CInterfaceManager *pIM= CInterfaceManager::getInstance();
825 sint32 srvVal= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_CYCLE)->getValue32();
826 return srvVal==(sint32)_PhraseCycleExecuteCounter;
829 // ***************************************************************************
830 void CSPhraseManager::sendMemorizeToServer(uint32 memoryLine, uint32 memorySlot, uint32 phraseId)
832 // Local simulation
833 if(ClientCfg.Local)
835 extern void debugUpdateActionBar();
836 debugUpdateActionBar();
838 // Send the memorize to server
839 else
841 CBitMemStream out;
842 const string sMsg = "PHRASE:MEMORIZE";
843 if(GenericMsgHeaderMngr.pushNameToStream(sMsg, out))
845 CSPhraseCom phrase= getPhrase(phraseId);
846 // free Band; don't send name
847 phrase.Name.clear();
849 uint8 memoryId= (uint8)memoryLine;
850 uint8 slotId= (uint8)memorySlot;
851 uint16 pid= (uint16)phraseId;
852 out.serial(memoryId);
853 out.serial(slotId);
854 out.serial(pid);
855 out.serial(phrase);
856 NetMngr.push(out);
857 //nlinfo("impulseCallBack : %s %d %d %d (phrase) sent", sMsg.c_str(), memoryId, slotId, pid);
859 else
860 nlwarning("impulseCallBack : unknown message name : '%s'.", sMsg.c_str());
864 // ***************************************************************************
865 void CSPhraseManager::sendForgetToServer(uint32 memoryLine, uint32 memoryIndex)
867 // Local simulation
868 if(ClientCfg.Local)
871 // Send the forget to server.
872 else
874 CBitMemStream out;
875 const string sMsg = "PHRASE:FORGET";
876 if(GenericMsgHeaderMngr.pushNameToStream(sMsg, out))
878 //serial the sentence memorized index
879 uint8 memoryId= (uint8)memoryLine;
880 uint8 slotId= (uint8)memoryIndex;
881 out.serial( memoryId );
882 out.serial( slotId );
883 NetMngr.push(out);
884 //nlinfo("impulseCallBack : %s %d %d sent", sMsg.c_str(), memoryId, slotId);
886 else
887 nlwarning("impulseCallBack : unknown message name : '%s'.", sMsg.c_str());
891 // ***************************************************************************
892 void CSPhraseManager::reset()
894 _PhraseMap.clear();
895 _PhraseClient.clear();
896 _FreeSlots.clear();
897 _Memories.clear();
899 // Default alloc.
900 _PhraseClient.resize(1024);
902 _InitInGameDone= false;
904 _SelectedMemoryDB= -1;
905 _SelectedMemoryDBalt = _SelectedMemoryDB;
906 // NB: slot under 2 can't be taken.
907 _MaxSlotSet= BookStartSlot-1;
908 _LastBookNumDbFill= 0;
909 _PhraseNextExecuteCounter= 0;
910 _PhraseCycleExecuteCounter= 0;
911 _CurrentExecuteLineNext= -1;
912 _CurrentExecuteSlotNext= -1;
913 _CurrentExecutePhraseIdNext= 0;
914 _CurrentExecuteLineCycle= -1;
915 _CurrentExecuteSlotCycle= -1;
916 _CurrentExecutePhraseIdCycle= 0;
917 _PhraseDebugEndNextAction= 0;
918 _PhraseDebugEndCyclicAction= 0;
919 _UserIndoor = true;
920 _BookSkillFitler = SKILLS::unknown;
921 _BookBrickTypeFilter = BRICK_TYPE::UNKNOWN;
923 _BookDbLeaves.clear();
924 for(uint i=0;i<NumProgressType;i++)
926 _ProgressionDbSheets[i].clear();
927 _ProgressionDbLevels[i].clear();
928 _ProgressionDbLocks[i].clear();
929 _LastProgressionNumDbFill[i]= 0;
931 _MemoryDbLeaves.clear();
932 _MemoryAltDbLeaves.clear();
933 _NextExecuteLeaf= NULL;
934 _NextExecuteIsCyclicLeaf= NULL;
936 _EquipInvalidationEnd= 0;
937 _CurrentServerTick= 0;
939 CompositionPhraseMemoryLineDest= 0;
940 CompositionPhraseMemoryLineDest= -1;
941 CompositionPhraseMemorySlotDest= -1;
943 // remove phrase progression update
944 CSBrickManager *pBM= CSBrickManager::getInstance();
945 CSkillManager *pSM= CSkillManager::getInstance();
946 pBM->removeBrickLearnedCallback(&_ProgressionUpdate);
947 pSM->removeSkillChangeCallback(&_ProgressionUpdate);
949 _TotalMalusEquipLeaf = NULL;
952 // ***************************************************************************
953 bool CSPhraseManager::isPhraseKnown(const CSPhraseCom &phrase) const
955 for(TPhraseMap::const_iterator it = _PhraseMap.begin(); it != _PhraseMap.end(); ++it)
957 if (it->second == phrase) return true;
959 return false;
962 // ***************************************************************************
963 string CSPhraseManager::formatMalus(sint base, sint malus)
965 if(malus)
966 return toString("@{F80F}%d@{FFFF} (%d)", base+malus, base);
967 else
968 return toString(base);
971 // ***************************************************************************
972 string CSPhraseManager::formatMalus(float base, float malus)
974 if(malus)
975 return toString("@{F80F}%.1f@{FFFF} (%.1f)", base+malus, base);
976 else
977 return toString("%.1f", base);
980 // ***************************************************************************
981 string CSPhraseManager::formatBonusMalus(sint32 base, sint32 mod)
983 string str;
984 if( mod == 0 )
986 str = toString("@{FFFF}") + toString(base);
988 else
989 if( mod > 0 ) // bonus
991 str = "@{0F0F}" + toString( base + mod )
992 + "@{FFFF}("
993 + toString( base )
994 + "@{0F0F} + "
995 + toString( mod )
996 + "@{FFFF})";
998 else
1000 str = "@{E42F}" + toString( base + mod )
1001 + "@{FFFF}("
1002 + toString( base )
1003 + "@{E42F} - "
1004 + toString( mod )
1005 + "@{FFFF})";
1007 return str;
1010 // ***************************************************************************
1011 void CSPhraseManager::buildPhraseDesc(string &text, const CSPhraseCom &phrase, uint32 phraseSheetId, bool wantRequirement, const std::string &specialPhraseFormat)
1013 CSBrickManager *pBM= CSBrickManager::getInstance();
1014 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1016 text.erase();
1018 // castable action?
1019 bool castable= phraseSheetId==0 || isPhraseCastable(phraseSheetId);
1022 // For true phraseId, or castable .sphrase only
1023 if(castable)
1025 // **** Get the format from EnergyType
1026 CSBrickSheet *rootBrick= NULL;
1027 if(phrase.Bricks.size())
1028 rootBrick= pBM->getBrick(phrase.Bricks[0]);
1029 if(rootBrick)
1031 static const string compoId= "composition";
1032 static const string compoTag("%compostart");
1033 bool isComposition= specialPhraseFormat==compoId;
1035 // if format not given by user, auto select it.
1036 if(specialPhraseFormat.empty() || isComposition)
1038 if(rootBrick->isCombat())
1039 text= CI18N::get("uihelpPhraseCombatFormat");
1040 else if(rootBrick->isMagic())
1041 text= CI18N::get("uihelpPhraseMagicFormat");
1042 else if(rootBrick->isFaber())
1043 text= CI18N::get("uihelpPhraseCraftFormat");
1044 else if(rootBrick->isSpecialPower())
1045 text= CI18N::get("uihelpPhraseSpecialPowerFormat");
1046 else if(rootBrick->isForageExtraction())
1047 text= CI18N::get("uihelpPhraseForageExtractionFormat");
1048 else if(rootBrick->isProcEnchantment())
1049 text= CI18N::get("uihelpPhraseProcEnchantment");
1050 else
1051 text= CI18N::get("uihelpPhraseOtherFormat");
1053 // if composition, cut the text before the tag (including)
1054 if(isComposition)
1056 string::size_type pos= text.find(compoTag);
1057 if(pos!=string::npos)
1058 text.erase(0, pos+compoTag.size());
1060 // else just clear the tag
1061 else
1063 strFindReplace(text, compoTag, string() );
1066 else
1067 text= CI18N::get(specialPhraseFormat);
1069 else
1070 return;
1072 // **** Phrase info basics
1073 // replace name
1074 strFindReplace(text, "%name", phrase.Name.toUtf8());
1075 // replace Sabrina Cost and credit.
1076 uint32 cost, credit;
1077 pBM->getSabrinaCom().getPhraseCost(phrase.Bricks, cost, credit);
1078 strFindReplace(text, "%cost", toString(cost));
1079 strFindReplace(text, "%credit", toString(credit));
1081 // for combat, fill weapon compatibility
1082 string weaponRestriction;
1083 bool usableWithMelee;
1084 bool usableWithRange;
1085 if(rootBrick && rootBrick->isCombat())
1087 getCombatWeaponRestriction(weaponRestriction, phrase, usableWithMelee, usableWithRange);
1088 strFindReplace(text, "%wcomp", weaponRestriction);
1091 // for Magic, fill Spell Level, and Magic Resist Type
1092 if(rootBrick && rootBrick->isMagic())
1094 // Spell Level
1095 uint32 spellLevel= getSpellLevel(phrase.Bricks);
1096 strFindReplace(text, "%mglvl", toString(spellLevel));
1098 // ResistMagic. May have mutliple because of double spell
1099 bool resistMagic[RESISTANCE_TYPE::NB_RESISTANCE_TYPE];
1100 getResistMagic(resistMagic, phrase.Bricks);
1101 bool first= true;
1102 string resList;
1103 for(uint i=0;i<RESISTANCE_TYPE::NB_RESISTANCE_TYPE;i++)
1105 if(resistMagic[i])
1107 if(first)
1108 first= false;
1109 else
1110 resList+= ", ";
1111 resList+= CI18N::get("rs"+ RESISTANCE_TYPE::toString((RESISTANCE_TYPE::TResistanceType)i) );
1114 // If have some resist
1115 if(!resList.empty())
1117 string fmt= CI18N::get("uihelpPhraseMagicResist");
1118 strFindReplace(fmt, "%t", resList);
1119 strFindReplace(text, "%magicresist", fmt);
1121 else
1123 strFindReplace(text, "%magicresist", string());
1128 // **** Compute Phrase Elements from phrase
1129 // get the current action malus (0-100)
1130 uint32 totalActionMalus= 0;
1131 CCDBNodeLeaf *actMalus = _TotalMalusEquipLeaf ? &*_TotalMalusEquipLeaf
1132 : &*(_TotalMalusEquipLeaf = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TOTAL_MALUS_EQUIP", false));
1134 // root brick must not be Power or aura, because Action malus don't apply to them
1135 // (ie leave 0 ActionMalus for Aura or Powers
1136 if(actMalus && !rootBrick->isSpecialPower())
1137 totalActionMalus= actMalus->getValue32();
1138 // Get the stats of the phrase, with malus due to item weared
1139 sint success= getPhraseSuccessRate(phrase);
1140 float castTime= 0, castTimeMalus= 0;
1141 sint range= 0, rangeMalus= 0;
1142 sint hpCost= 0, hpCostMalus= 0;
1143 sint enCost= 0, enCostMalus= 0;
1145 getPhraseCastTime(phrase, totalActionMalus, castTime, castTimeMalus);
1146 getPhraseMagicRange(phrase, totalActionMalus, range, rangeMalus);
1147 getPhraseHpCost(phrase, totalActionMalus, hpCost, hpCostMalus);
1149 if(rootBrick->isCombat())
1150 getPhraseStaCost(phrase, totalActionMalus, enCost, enCostMalus);
1151 else if(rootBrick->isMagic())
1152 getPhraseSapCost(phrase, totalActionMalus, enCost, enCostMalus);
1153 else
1154 getPhraseFocusCost(phrase, totalActionMalus, enCost, enCostMalus);
1156 sint32 successModifier = 0;
1157 CCDBNodeLeaf * nodeSM = NULL;
1158 if(rootBrick->isCombat())
1160 // if phrase can be used with in melee and range we choose which one to display according to hand weapon family
1161 if( usableWithMelee && usableWithRange )
1163 CInventoryManager *inv = CInventoryManager::getInstance();
1164 if(inv)
1166 uint32 rightHandSheet = inv->getRightHandItemSheet();
1167 if( inv->isRangeWeaponItem(rightHandSheet) )
1169 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:RANGE", false);
1171 else
1173 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MELEE", false);
1177 else
1178 // phrase usable only in melee fight
1179 if( usableWithMelee )
1181 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MELEE", false);
1183 else
1184 // phrase usable only in range fight
1185 if( usableWithRange )
1187 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:RANGE", false);
1190 else
1191 if(rootBrick->isMagic())
1193 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MAGIC", false);
1195 if(nodeSM)
1197 successModifier = nodeSM->getValue32();
1199 string successStr = formatBonusMalus(success, successModifier);
1201 // **** assemble in text
1202 strFindReplace(text, "%success", toString(successStr));
1203 strFindReplace(text, "%duration", formatMalus(castTime, castTimeMalus) );
1204 strFindReplace(text, "%energy_cost", formatMalus(enCost, enCostMalus) );
1205 strFindReplace(text, "%hp_cost", formatMalus(hpCost, hpCostMalus) );
1206 // special range and "self"
1207 if(range==0)
1208 strFindReplace(text, "%range", CI18N::get("uihelpPhraseRangeSelf"));
1209 else
1211 string fmt= CI18N::get("uihelpPhraseRangeMeters");
1212 strFindReplace(fmt, "%dist", formatMalus(range, rangeMalus));
1213 strFindReplace(text, "%range", fmt);
1216 // **** special Forage extraction
1217 if(rootBrick->isForageExtraction())
1219 // Choose the fmt text
1220 string fmt= getForageExtractionPhraseEcotypeFmt(phrase);
1222 // Replace forage success rate in any ecotype
1223 successModifier = 0;
1224 sint32 commonModifier = 0;
1225 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:0:FORAGE", false);
1226 if(nodeSM)
1228 commonModifier = nodeSM->getValue32();
1230 //desert
1231 success= getForageExtractionPhraseSuccessRate(phrase, SKILLS::SHFDAEM);
1232 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:1:FORAGE", false);
1233 if(nodeSM) successModifier = nodeSM->getValue32();
1234 if( successModifier == 0 )
1235 successModifier = commonModifier;
1236 successStr = formatBonusMalus(success, successModifier);
1237 successModifier = 0;
1238 strFindReplace(fmt, "%suc_desert", toString(successStr));
1240 //forest
1241 success= getForageExtractionPhraseSuccessRate(phrase, SKILLS::SHFFAEM);
1242 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:2:FORAGE", false);
1243 if(nodeSM) successModifier = nodeSM->getValue32();
1244 if( successModifier == 0 )
1245 successModifier = commonModifier;
1246 successStr = formatBonusMalus(success, successModifier);
1247 successModifier = 0;
1248 strFindReplace(fmt, "%suc_forest", successStr);
1250 // lake
1251 success= getForageExtractionPhraseSuccessRate(phrase, SKILLS::SHFLAEM);
1252 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:3:FORAGE", false);
1253 if(nodeSM) successModifier = nodeSM->getValue32();
1254 if( successModifier == 0 )
1255 successModifier = commonModifier;
1256 successStr = formatBonusMalus(success, successModifier);
1257 successModifier = 0;
1258 strFindReplace(fmt, "%suc_lake", successStr);
1260 // jungle
1261 success= getForageExtractionPhraseSuccessRate(phrase, SKILLS::SHFJAEM);
1262 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:4:FORAGE", false);
1263 if(nodeSM) successModifier = nodeSM->getValue32();
1264 if( successModifier == 0 )
1265 successModifier = commonModifier;
1266 successStr = formatBonusMalus(success, successModifier);
1267 successModifier = 0;
1268 strFindReplace(fmt, "%suc_jungle", successStr);
1270 //prime roots
1271 success= getForageExtractionPhraseSuccessRate(phrase, SKILLS::SHFPAEM);
1272 nodeSM = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:ECO:6:FORAGE", false);
1273 if(nodeSM) successModifier = nodeSM->getValue32();
1274 if( successModifier == 0 )
1275 successModifier = commonModifier;
1276 successStr = formatBonusMalus(success, successModifier);
1277 successModifier = 0;
1278 strFindReplace(fmt, "%suc_prime", successStr);
1280 // replace the forage succes
1281 strFindReplace(text, "%suc_forage", fmt);
1284 // **** not castable case
1285 else
1287 // special for charac buying
1288 if(isPhraseCharacBuying(phraseSheetId))
1289 text= CI18N::get("uihelpPhraseCharacteristic");
1290 else
1291 text= CI18N::get("uihelpPhraseNotCastableFormat");
1294 // **** Special .sphrase description
1295 if (phraseSheetId)
1297 // get the text
1298 string desc = STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedDescription(CSheetId(phraseSheetId));
1299 if (desc.empty())
1300 strFindReplace(text, "%desc", string());
1301 else
1303 // append an \n before, for clearness
1304 desc= string("\n") + desc;
1305 // append \n at end if not done
1306 if(desc[desc.size()-1]!='\n')
1307 desc+= '\n';
1308 // replace in format
1309 strFindReplace(text, "%desc", desc);
1312 else
1314 strFindReplace(text, "%desc", string());
1317 // **** Special .sphrase requirement
1318 if(phraseSheetId && wantRequirement)
1320 string reqText;
1321 reqText= CI18N::get("uihelpPhraseRequirementHeader");
1323 // replace the skill point cost
1324 strFindReplace(reqText, "%sp", toString(getCurrentPhraseSheetPrice(phraseSheetId)));
1325 strFindReplace(reqText, "%basesp", toString(getBasePhraseSheetPrice(phraseSheetId)));
1327 // build the phrase requirement from this sheetId
1328 TPhraseReqSkillMap::const_iterator it= _PhraseReqSkillMap.find(phraseSheetId);
1329 if(it!=_PhraseReqSkillMap.end())
1331 const CReqSkillFormula &formula= it->second;
1333 // from this formula, build the requirement tex
1334 string textForm;
1335 formula.getInfoText(textForm);
1336 reqText+= textForm;
1339 // replace in text
1340 strFindReplace(text, "%req", reqText);
1342 else
1343 strFindReplace(text, "%req", "");
1347 // ***************************************************************************
1348 sint CSPhraseManager::getPhraseSuccessRate(const CSPhraseCom &phrase)
1350 CSBrickManager *pBM= CSBrickManager::getInstance();
1351 CSkillManager *pSM= CSkillManager::getInstance();
1353 if(phrase.Bricks.empty())
1354 return 0;
1355 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
1356 if(!rootBrick)
1357 return 0;
1359 // get the Skill value (according to phrase type etc...)
1360 TSuccessTable sTable;
1361 sint skillValue= 0;
1362 if(rootBrick->isCombat())
1364 // retrieve the current Skill of the Item in RightHand
1365 SKILLS::ESkills skill= getRightHandItemSkill();
1367 // Get its value
1368 skillValue= pSM->getSkillValueMaxBranch(skill);
1370 // use the combat table
1371 sTable= STCombat;
1373 else if(rootBrick->isMagic())
1375 uint32 numSkill= 0;
1376 skillValue= 0;
1378 // Must take a mean of all skills
1379 for(uint i=0;i<phrase.Bricks.size();i++)
1381 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
1382 if(brick)
1384 SKILLS::ESkills skill= brick->getSkill();
1385 if(skill!=SKILLS::unknown)
1387 skillValue+= pSM->getSkillValueMaxBranch(skill);
1388 numSkill++;
1393 // The mean!
1394 if(numSkill)
1395 skillValue/= numSkill;
1397 // use one of the magic table
1398 if(rootBrick->ActionNature==ACTNATURE::CURATIVE_MAGIC)
1399 sTable= STCurativeMagic;
1400 else
1401 // should be ACTNATURE::OFFENSIVE_MAGIC !!
1402 sTable= STOffensiveMagic;
1404 // Craft: false, but return value not used
1405 else if(rootBrick->isFaber())
1407 skillValue= pSM->getSkillValueMaxBranch(rootBrick->getSkill());
1408 sTable= STCraft;
1410 // Forage Prospect: always success
1411 else if(rootBrick->isForageProspection())
1413 return 100;
1415 // Forage Extract: false, but return value not used
1416 else if(rootBrick->isForageExtraction())
1418 skillValue= pSM->getSkillValueMaxBranch(rootBrick->getSkill());
1419 sTable= STExtract;
1421 // Default
1422 else
1424 // Default: take skill value of the root
1425 skillValue= pSM->getSkillValueMaxBranch(rootBrick->getSkill());
1426 // well, it's bug time!
1427 sTable= STCombat;
1430 // return the success rate with this skillValue
1431 return getPhraseSuccessRate(sTable, phrase, skillValue, INT_MAX);
1434 // ***************************************************************************
1435 sint CSPhraseManager::getCraftPhraseSuccessRate(const CSPhraseCom &phrase, SKILLS::ESkills skill, uint minMpLevel, sint successModifier)
1437 CSkillManager *pSM= CSkillManager::getInstance();
1439 if(phrase.Bricks.empty())
1440 return 0;
1442 // take skill value of the skill
1443 sint skillValue= pSM->getBestSkillValue(skill);
1445 // apply success rate modifier from server
1446 skillValue += successModifier;
1448 // return the sr according to this skill
1449 return getPhraseSuccessRate(STCraft, phrase, skillValue, minMpLevel);
1452 // ***************************************************************************
1453 sint CSPhraseManager::getForageExtractionPhraseSuccessRate(const CSPhraseCom &phrase, SKILLS::ESkills skill)
1455 CSkillManager *pSM= CSkillManager::getInstance();
1457 if(phrase.Bricks.empty())
1458 return 0;
1460 // take skill value of the skill
1461 sint skillValue= pSM->getSkillValueMaxBranch(skill);
1463 // return the sr according to this skill
1464 return getPhraseSuccessRate(STExtract, phrase, skillValue, INT_MAX);
1467 // ***************************************************************************
1468 void CSPhraseManager::getPhraseSapCost(const CSPhraseCom &phrase, uint32 totalActionMalus, sint &cost, sint &costMalus)
1470 CSBrickManager *pBM= CSBrickManager::getInstance();
1471 cost= (sint)getPhraseSumBrickProp(phrase, pBM->SapPropId, true);
1472 // compute malus (positive)
1473 costMalus= (cost * (totalActionMalus))/100;
1476 // ***************************************************************************
1477 void CSPhraseManager::getPhraseStaCost(const CSPhraseCom &phrase, uint32 totalActionMalus, sint &cost, sint &costMalus)
1479 CSBrickManager *pBM= CSBrickManager::getInstance();
1480 cost= (sint)getPhraseSumBrickProp(phrase, pBM->StaPropId, true);
1481 // TODO: combat special case
1482 // compute malus (positive)
1483 costMalus= (cost * (totalActionMalus))/100;
1486 // ***************************************************************************
1487 void CSPhraseManager::getPhraseFocusCost(const CSPhraseCom &phrase, uint32 totalActionMalus, sint &cost, sint &costMalus)
1489 CSBrickManager *pBM= CSBrickManager::getInstance();
1490 cost= (sint)getPhraseSumBrickProp(phrase, pBM->FocusPropId, true);
1491 // compute malus (positive)
1492 costMalus= (cost * (totalActionMalus))/100;
1495 // ***************************************************************************
1496 void CSPhraseManager::getPhraseHpCost(const CSPhraseCom &phrase, uint32 totalActionMalus, sint &cost, sint &costMalus)
1498 CSBrickManager *pBM= CSBrickManager::getInstance();
1499 cost= (sint)getPhraseSumBrickProp(phrase, pBM->HpPropId, true);
1500 // compute malus (positive)
1501 costMalus= (cost * (totalActionMalus))/100;
1504 // ***************************************************************************
1505 void CSPhraseManager::getPhraseCastTime(const CSPhraseCom &phrase, uint32 totalActionMalus, float &castTime, float &castTimeMalus)
1507 CSBrickManager *pBM= CSBrickManager::getInstance();
1509 castTime= 0;
1510 castTimeMalus= 0;
1512 if(phrase.Bricks.empty())
1513 return;
1514 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
1515 if(!rootBrick)
1516 return;
1518 // for all bricks get the max "min and Max" cast time, and get the sum of time credit
1519 uint sumCastTimeCredit= 0;
1520 uint sabrinaCost= 0;
1521 float sabrinaRelativeCost = 1.0f;
1522 uint minCastTime= 0, maxCastTime= 0;
1523 uint i;
1524 for(i=0;i<phrase.Bricks.size();i++)
1526 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
1527 if(brick)
1529 // For cast time, take the max
1530 minCastTime= max(minCastTime, (uint)brick->MinCastTime);
1531 maxCastTime= max(maxCastTime, (uint)brick->MaxCastTime);
1533 if(brick->SabrinaCost>0)
1534 sabrinaCost+= brick->SabrinaCost;
1536 if( brick->SabrinaRelativeCost>0.f)
1537 sabrinaRelativeCost+= brick->SabrinaRelativeCost;
1539 for(uint j=0;j<brick->Properties.size();j++)
1541 if(brick->Properties[j].PropId == pBM->CastTimePropId)
1543 sumCastTimeCredit+= abs(brick->SabrinaCost);
1549 // TODO yoyo: may change in future?
1550 if(rootBrick->isFaber())
1551 minCastTime= maxCastTime= 20;
1553 // Code from Server!!
1554 float castingTimeFactor;
1555 if(sabrinaCost)
1556 castingTimeFactor = (float)sumCastTimeCredit/(float)(sabrinaCost*sabrinaRelativeCost);
1557 else
1558 castingTimeFactor = (float)sumCastTimeCredit;
1559 clamp(castingTimeFactor, 0.f, 1.f);
1560 // Check Min >= Max
1561 if(maxCastTime < minCastTime)
1562 maxCastTime = minCastTime;
1564 // Compute the casting time.
1565 castTime= (float)(maxCastTime-minCastTime)*castingTimeFactor+(float)minCastTime;
1567 // Compute the malus (positive value)
1568 castTimeMalus= (float)(maxCastTime-minCastTime)*castingTimeFactor*totalActionMalus/100;
1570 // In bricks, save in (1/10sec)
1571 castTime/= 10;
1572 castTimeMalus/= 10;
1575 // ***************************************************************************
1576 void CSPhraseManager::getPhraseMagicRange(const CSPhraseCom &phrase, uint32 totalActionMalus, sint &range, sint &rangeMalus)
1578 CSBrickManager *pBM= CSBrickManager::getInstance();
1580 range= 0;
1581 rangeMalus= 0;
1583 if(phrase.Bricks.empty())
1584 return;
1585 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
1586 if(!rootBrick)
1587 return;
1589 // for all bricks get the min "min and Max" range, and get the sum of range credit
1590 uint sumRangeCredit= 0;
1591 uint sabrinaCost= 0;
1592 float sabrinaRelativeCost= 1.f;
1593 uint minRange= 255, maxRange= 255;
1594 uint i;
1595 for(i=0;i<phrase.Bricks.size();i++)
1597 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
1598 if(brick)
1600 // For range, take the min
1601 minRange= min(minRange, (uint)brick->MinRange);
1602 maxRange= min(maxRange, (uint)brick->MaxRange);
1604 if(brick->SabrinaCost>0)
1605 sabrinaCost+= brick->SabrinaCost;
1607 if(brick->SabrinaRelativeCost>0.f)
1608 sabrinaRelativeCost+= brick->SabrinaRelativeCost;
1610 for(uint j=0;j<brick->Properties.size();j++)
1612 if(brick->Properties[j].PropId == pBM->RangePropId)
1614 // For Range, must add the Creidt Value of the prop
1615 sumRangeCredit+= (uint)fabs(brick->Properties[j].Value);
1621 // Code from Server!!
1622 float rangeFactor;
1623 if(sabrinaCost)
1624 rangeFactor = (float)sumRangeCredit/(float)(sabrinaCost*sabrinaRelativeCost);
1625 else
1626 rangeFactor = (float)sumRangeCredit;
1627 clamp(rangeFactor, 0.f, 1.f);
1628 // Check Min >= Max
1629 if(maxRange < minRange)
1630 maxRange = minRange;
1631 // Compute the range. the more the credit, the lesser the range
1632 float fRange = (float)(maxRange-minRange)*(1-rangeFactor)+(float)minRange;
1634 // Compute the range, with malus
1635 float fRangeWithMalus = (float)(maxRange-minRange)*(1-rangeFactor)*100/(100+totalActionMalus)+(float)minRange;
1637 // In bricks, save in meters
1638 range= (sint)fRange;
1639 rangeMalus= (sint)fRangeWithMalus - range;
1642 // ***************************************************************************
1643 float CSPhraseManager::getPhraseSumBrickProp(const CSPhraseCom &phrase, uint propId, bool doAbs)
1645 CSBrickManager *pBM= CSBrickManager::getInstance();
1647 float sum= 0.f;
1648 for(uint i=0;i<phrase.Bricks.size();i++)
1650 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
1651 if(brick)
1653 // test all properties
1654 for(uint j=0;j<brick->Properties.size();j++)
1656 if(brick->Properties[j].PropId==propId)
1658 if(doAbs)
1659 sum+= (float)fabs(brick->Properties[j].Value);
1660 else
1661 sum+= brick->Properties[j].Value;
1663 else if(propId==CSBrickManager::getInstance()->StaPropId && brick->Properties[j].PropId==CSBrickManager::getInstance()->StaWeightFactorId)
1665 CInterfaceManager *im = CInterfaceManager::getInstance();
1666 if (!_ServerUserDefaultWeightHandsLeaf) _ServerUserDefaultWeightHandsLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:DEFAULT_WEIGHT_HANDS");
1667 uint32 weight = (uint32)(&*_ServerUserDefaultWeightHandsLeaf)->getValue32() / 10; // weight must be in dg here
1668 CDBCtrlSheet *ctrlSheet = dynamic_cast<CDBCtrlSheet *>(CWidgetManager::getInstance()->getElementFromId("ui:interface:gestionsets:hands:handr"));
1669 if (ctrlSheet)
1671 const CItemSheet *itemSheet = ctrlSheet->asItemSheet();
1672 if (itemSheet && (itemSheet->Family == ITEMFAMILY::MELEE_WEAPON || itemSheet->Family == ITEMFAMILY::RANGE_WEAPON))
1674 weight = (uint32) ctrlSheet->getItemWeight();
1677 ctrlSheet = dynamic_cast<CDBCtrlSheet *>(CWidgetManager::getInstance()->getElementFromId("ui:interface:gestionsets:hands:handl"));
1678 if (ctrlSheet)
1680 const CItemSheet *itemSheet = ctrlSheet->asItemSheet();
1681 if (itemSheet && (itemSheet->Family == ITEMFAMILY::MELEE_WEAPON /*|| itemSheet->Family == ITEMFAMILY::AMMO*/))
1683 weight = (weight + (uint32) ctrlSheet->getItemWeight()) /*/ 2*/;
1687 // float fweight = (float)weight / DB_WEIGHT_SCALE;
1688 float value = brick->Properties[j].Value;
1689 sint32 value2 = sint32(brick->Properties[j].Value2);
1691 if(doAbs)
1692 sum+= (float)fabs(value * weight / DB_WEIGHT_SCALE + value2);
1693 else
1694 sum+= brick->Properties[j].Value * weight / DB_WEIGHT_SCALE + brick->Properties[j].Value2;
1700 return sum;
1704 // ***************************************************************************
1705 bool CSPhraseManager::isExecutionInvalidCauseTempInv() const
1707 CTempInvManager *pTIM = CTempInvManager::getInstance();
1708 // Never invalidate action if we are in forage mode
1709 if (pTIM->getMode() == TEMP_INV_MODE::Forage)
1710 return false;
1711 // Invalid action if the temp inv is opened
1712 return CTempInvManager::getInstance()->isOpened();
1715 // ***************************************************************************
1716 sint CSPhraseManager::getSuccessRate(TSuccessTable st, sint level, bool partialSuccess)
1718 nlassert(st>=0 && st<NumSuccessTable);
1719 if(_SuccessTableSheet[st])
1721 sint bestProba= 50;
1722 sint bestLevelDiff= INT_MAX;
1724 for(uint i=0;i<_SuccessTableSheet[st]->SuccessTable.size();i++)
1726 sint levelDiff= level - _SuccessTableSheet[st]->SuccessTable[i].RelativeLevel;
1727 levelDiff= abs(levelDiff);
1728 if(levelDiff<bestLevelDiff)
1730 bestLevelDiff= levelDiff;
1731 if(partialSuccess)
1732 bestProba= _SuccessTableSheet[st]->SuccessTable[i].PartialSuccessProbability;
1733 else
1734 bestProba= _SuccessTableSheet[st]->SuccessTable[i].SuccessProbability;
1738 return bestProba;
1740 else
1741 return 0;
1744 // ***************************************************************************
1745 void CSPhraseManager::loadSuccessTable()
1747 // load all the table from packed sheet
1748 _SuccessTableSheet[STCombat]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("fight_phrase.succes_chances_table")));
1749 _SuccessTableSheet[STOffensiveMagic]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("offensive_magic.succes_chances_table")));
1750 _SuccessTableSheet[STCurativeMagic]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("curative_magic.succes_chances_table")));
1751 _SuccessTableSheet[STCraft]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("craft.succes_chances_table")));
1752 _SuccessTableSheet[STExtract]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("extracting.succes_chances_table")));
1753 _SuccessTableSheet[STResistMagic]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("magic_resist.succes_chances_table")));
1754 _SuccessTableSheet[STResistMagicLink]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("magic_resist_link.succes_chances_table")));
1755 _SuccessTableSheet[STDodgeParry]= dynamic_cast<CSuccessTableSheet*>(SheetMngr.get(CSheetId("dodge_parry.succes_chances_table")));
1758 // ***************************************************************************
1759 void CSPhraseManager::update()
1761 // Update brick state ?
1762 if (UserEntity)
1764 bool userIndoor = UserEntity->indoor();
1765 if (_UserIndoor != userIndoor)
1767 _UserIndoor = userIndoor;
1768 updateAllMemoryCtrlState();
1773 // ***************************************************************************
1774 sint CSPhraseManager::getPhraseSuccessRate(TSuccessTable st, const CSPhraseCom &phrase, sint skillValue, uint minMpLevel)
1776 if(phrase.empty())
1777 return 0;
1779 CSBrickManager *pBM= CSBrickManager::getInstance();
1781 // get the Sabrina cost
1782 uint32 costSum, creditSum;
1783 pBM->getSabrinaCom().getPhraseCost(phrase.Bricks, costSum, creditSum);
1784 // get the max cost of all brick
1785 uint32 costMax= pBM->getSabrinaCom().getPhraseMaxBrickCost(phrase.Bricks);
1787 // Special For combat: maybe replace the costSum
1788 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
1789 if(rootBrick && rootBrick->isCombat())
1791 // this to avoid problem with the default attack which has a 0 cost...
1792 costSum= max(costSum, getRightHandEffectiveLevel());
1796 // New Formula to get the Entry in Difficulty table
1797 sint deltaLevel;
1798 // special for craft (credit is actualy the level of item crafted)
1799 if(st==STCraft)
1800 deltaLevel= skillValue - min((sint)creditSum,(sint)minMpLevel);
1801 // general case
1802 else
1803 deltaLevel= skillValue + (sint)creditSum - (sint)costSum - (sint)costMax;
1805 return getSuccessRate(st, deltaLevel);
1808 // ***************************************************************************
1809 void CSPhraseManager::updateExecutionDisplay()
1811 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1813 // **** test if have to draws the icons, and try to recover if sphrase forgeted or removed.
1815 // do we have to display icons?
1816 bool displayCycle= _CurrentExecuteSlotCycle>=0 && _SelectedMemoryDB==_CurrentExecuteLineCycle;
1817 bool displayNext= _CurrentExecuteSlotNext>=0 && _SelectedMemoryDB==_CurrentExecuteLineNext;
1819 // if yes, verify that the actions under the icons are relevant
1820 if(displayCycle)
1822 // if the memory slot is different from the action executed
1823 if(_Memories[_CurrentExecuteLineCycle].Slot[_CurrentExecuteSlotCycle].Id != _CurrentExecutePhraseIdCycle)
1825 // fails!
1826 displayCycle= false;
1828 // try to find in the action bar, this action (in case of DragAndDrop for instance)
1829 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
1831 if(_Memories[_CurrentExecuteLineCycle].Slot[i].Id == _CurrentExecutePhraseIdCycle)
1833 // change the slot number
1834 _CurrentExecuteSlotCycle= i;
1835 // ok and stop
1836 displayCycle= true;
1837 break;
1843 // if yes, verify that the actions under the icons are relevant
1844 if(displayNext)
1846 // if the memory slot is different from the action executed
1847 if(_Memories[_CurrentExecuteLineNext].Slot[_CurrentExecuteSlotNext].Id != _CurrentExecutePhraseIdNext)
1849 // fails!
1850 displayNext= false;
1852 // try to find in the action bar, this action (in case of DragAndDrop for instance)
1853 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
1855 if(_Memories[_CurrentExecuteLineNext].Slot[i].Id == _CurrentExecutePhraseIdNext)
1857 // change the slot number
1858 _CurrentExecuteSlotNext= i;
1859 // ok and stop
1860 displayNext= true;
1861 break;
1868 // **** display the icons in the action bar
1870 // Actually, if the NextAction is the same as the cycle one, hide next one
1871 if(displayCycle && displayNext &&
1872 _CurrentExecuteLineNext == _CurrentExecuteLineCycle &&
1873 _CurrentExecuteSlotNext == _CurrentExecuteSlotCycle )
1874 displayNext= false;
1876 // DisplayCycleSelectionOnActionBar
1877 CInterfaceElement *viewCycle= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewCycleAction);
1878 if(viewCycle)
1880 CInterfaceElement *ctrl= NULL;
1881 if(displayCycle)
1882 ctrl= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewSlotBase + toString(_CurrentExecuteSlotCycle));
1883 if(displayCycle && ctrl)
1885 viewCycle->setParentPos(ctrl);
1886 viewCycle->setActive(true);
1887 viewCycle->invalidateCoords();
1889 else
1891 viewCycle->setActive(false);
1895 // DisplayNextSelectionOnActionBar
1896 CInterfaceElement *viewNext= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewNextAction);
1897 if(viewNext)
1899 CInterfaceElement *ctrl= NULL;
1900 if(displayNext)
1901 ctrl= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewSlotBase + toString(_CurrentExecuteSlotNext));
1902 if(displayNext && ctrl)
1904 viewNext->setParentPos(ctrl);
1905 viewNext->setActive(true);
1906 viewNext->invalidateCoords();
1908 else
1910 viewNext->setActive(false);
1915 // **** Display Next Action on Slot in PLAYER window , whatever the selected memory line is
1916 if(_CurrentExecutePhraseIdCycle>0 || _CurrentExecutePhraseIdNext>0)
1918 // get the next phrase
1919 uint nextPhrase;
1920 if(_CurrentExecutePhraseIdNext>0)
1921 nextPhrase= _CurrentExecutePhraseIdNext;
1922 else
1923 nextPhrase= _CurrentExecutePhraseIdCycle;
1925 // display this slot
1926 _NextExecuteLeaf->setValue32(nextPhrase);
1928 // display or not the widget "this is a cyclic action"
1929 _NextExecuteIsCyclicLeaf->setValue32(_CurrentExecutePhraseIdNext==0 || _CurrentExecutePhraseIdNext==_CurrentExecutePhraseIdCycle);
1931 else
1933 _NextExecuteLeaf->setValue32(0);
1934 _NextExecuteIsCyclicLeaf->setValue32(0);
1938 // ***************************************************************************
1939 void CSPhraseManager::clientExecute(uint32 memoryLine, uint32 memorySlot, bool cyclic)
1941 if(cyclic)
1943 // Inc Local Counter For Server/Client synchronisation
1944 _PhraseCycleExecuteCounter++;
1945 _PhraseCycleExecuteCounter&=PHRASE_EXECUTE_COUNTER_MASK;
1946 // what slot set?
1947 _CurrentExecuteLineCycle= memoryLine;
1948 _CurrentExecuteSlotCycle= memorySlot;
1949 _CurrentExecutePhraseIdCycle= getMemorizedPhrase(memoryLine, memorySlot);
1951 else
1953 // Inc Local Counter For Server/Client synchronisation
1954 _PhraseNextExecuteCounter++;
1955 _PhraseNextExecuteCounter&=PHRASE_EXECUTE_COUNTER_MASK;
1956 // what slot set?
1957 _CurrentExecuteLineNext= memoryLine;
1958 _CurrentExecuteSlotNext= memorySlot;
1959 _CurrentExecutePhraseIdNext= getMemorizedPhrase(memoryLine, memorySlot);
1962 // update the Action Execution view
1963 updateExecutionDisplay();
1965 // Local simulation
1966 if(ClientCfg.Local)
1968 if(cyclic)
1969 _PhraseDebugEndCyclicAction= T1 + 8000;
1970 else
1971 _PhraseDebugEndNextAction= T1 + 2000;
1975 // ***************************************************************************
1976 void CSPhraseManager::cancelClientExecute(bool cyclic)
1978 if(cyclic)
1980 // Dec Local Counter For Server/Client synchronisation
1981 _PhraseCycleExecuteCounter--;
1982 _PhraseCycleExecuteCounter&=PHRASE_EXECUTE_COUNTER_MASK;
1983 // reset
1984 _CurrentExecuteLineCycle= -1;
1985 _CurrentExecuteSlotCycle= -1;
1986 _CurrentExecutePhraseIdCycle= 0;
1988 else
1990 // Dec Local Counter For Server/Client synchronisation
1991 _PhraseNextExecuteCounter--;
1992 _PhraseNextExecuteCounter&=PHRASE_EXECUTE_COUNTER_MASK;
1993 // what slot set?
1994 _CurrentExecuteLineNext= -1;
1995 _CurrentExecuteSlotNext= -1;
1996 _CurrentExecutePhraseIdNext= 0;
1999 // update the Action Execution view
2000 updateExecutionDisplay();
2004 // ***************************************************************************
2005 void CSPhraseManager::sendExecuteToServer(uint32 memoryLine, uint32 memorySlot, bool cyclic)
2007 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2009 // Local simulation
2010 if(ClientCfg.Local)
2012 if(cyclic)
2013 pIM->displaySystemInfo("PhraseCycleCasted:"+toString(memoryLine)+"_"+toString(memorySlot), "CHK");
2014 else
2015 pIM->displaySystemInfo("PhraseNextCasted:"+toString(memoryLine)+"_"+toString(memorySlot), "CHK");
2017 extern void debugUpdateActionBar();
2018 debugUpdateActionBar();
2020 // Send the execute to server.
2021 else
2023 // before, append the execution counter to the list of ACK to wait
2024 appendCurrentToAckExecute(cyclic);
2026 // send msg
2027 CBitMemStream out;
2028 const char *msg;
2029 if(cyclic)
2030 msg= "PHRASE:EXECUTE_CYCLIC";
2031 else
2032 msg= "PHRASE:EXECUTE";
2033 if(GenericMsgHeaderMngr.pushNameToStream(msg, out))
2035 //serial the sentence memorized index
2036 uint8 memoryId= (uint8)memoryLine;
2037 uint8 slotId= (uint8)memorySlot;
2038 out.serial( memoryId );
2039 out.serial( slotId );
2040 NetMngr.push(out);
2042 else
2043 nlinfo("unknown message %s", msg);
2047 // ***************************************************************************
2048 void CSPhraseManager::executeCraft(uint32 memoryLine, uint32 memorySlot, uint32 planSheetId,
2049 std::vector<CFaberMsgItem> &mpItemPartList, std::vector<CFaberMsgItem> specificItemList)
2051 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2053 // **** Do also a clientExecute to display the correct craft action (NB: not cyclic!)
2054 clientExecute(memoryLine, memorySlot, false);
2056 // **** Then do a special sendExecuteToServer
2057 // Local simulation
2058 if(ClientCfg.Local)
2060 pIM->displaySystemInfo("PhraseCraftCasted:"+toString(memoryLine)+"_"+toString(memorySlot), "CHK");
2061 extern void debugUpdateActionBar();
2062 debugUpdateActionBar();
2064 // Send the execute to the server
2065 else
2067 // before, append the execution counter to the list of ACK to wait
2068 appendCurrentToAckExecute(false);
2070 // send msg
2071 CBitMemStream out;
2072 const char *msg;
2073 msg= "PHRASE:EXECUTE_FABER";
2074 if(GenericMsgHeaderMngr.pushNameToStream(msg, out))
2076 // Serialize the faberPlan first.
2077 out.serial( planSheetId );
2078 // Serial the sentence memorized index
2079 uint8 memoryId= (uint8)memoryLine;
2080 uint8 slotId= (uint8)memorySlot;
2081 out.serial( memoryId );
2082 out.serial( slotId );
2083 // Then serial the list of Mp index
2084 out.serialCont( mpItemPartList );
2085 out.serialCont( specificItemList );
2086 NetMngr.push(out);
2088 else
2089 nlinfo("unknown message %s", msg);
2095 // ***************************************************************************
2096 void CSPhraseManager::executeCristalize(uint32 memoryLine, uint32 memorySlot)
2098 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2100 // **** Increment counter client side (like an execution) (no cylic)
2101 clientExecute(memoryLine, memorySlot, false);
2103 // **** special sendExecuteToServer
2104 if(ClientCfg.Local)
2106 pIM->displaySystemInfo("PhraseCristalize:"+toString(memoryLine)+"_"+toString(memorySlot), "CHK");
2107 extern void debugUpdateActionBar();
2108 debugUpdateActionBar();
2110 else
2112 // before, append the execution counter to the list of ACK to wait
2113 appendCurrentToAckExecute(false);
2115 // send msg
2116 CBitMemStream out;
2117 const string sMsg = "PHRASE:CRISTALIZE";
2118 if(GenericMsgHeaderMngr.pushNameToStream(sMsg, out))
2120 //serial the sentence memorized index
2121 uint8 memoryId= (uint8)memoryLine;
2122 uint8 slotId= (uint8)memorySlot;
2123 out.serial( memoryId );
2124 out.serial( slotId );
2125 NetMngr.push(out);
2127 else
2128 nlinfo("unknown message %s", sMsg.c_str());
2133 // ***************************************************************************
2134 void CSPhraseManager::executeDefaultAttack()
2136 // Try to find a "default attack" in the memories, and prefer launch it instead
2137 uint32 memoryLine, memorySlot;
2138 if(findDefaultAttack(memoryLine, memorySlot))
2140 clientExecute(memoryLine, memorySlot, true);
2141 sendExecuteToServer(memoryLine, memorySlot, true);
2143 else
2145 // Set the database in local.
2146 if(ClientCfg.Local)
2148 IngameDbMngr.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_MODE), MBEHAV::COMBAT); // Mode
2149 UserEntity->updateVisualProperty(0, CLFECOMMON::PROPERTY_MODE);
2151 else
2153 // Attack MSG.
2154 const string msgName = "COMBAT:DEFAULT_ATTACK";
2155 CBitMemStream out;
2156 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2157 NetMngr.push(out);
2158 else
2159 nlwarning("UE::attack: unknown message named '%s'.", msgName.c_str());
2165 // ***************************************************************************
2166 void CSPhraseManager::appendCurrentToAckExecute(bool cyclic)
2168 CAckExecuteEntry entry;
2170 // wait an ack from server
2171 entry.State= CAckExecuteEntry::WaitAck;
2172 if(cyclic)
2174 entry.Counter= _PhraseCycleExecuteCounter;
2175 entry.MemoryLine= _CurrentExecuteLineCycle;
2176 entry.MemorySlot= _CurrentExecuteSlotCycle;
2177 entry.PhraseId= _CurrentExecutePhraseIdCycle;
2178 _AckExecuteCycleList.push_back(entry);
2179 #ifdef PHRASE_DEBUG_VERBOSE
2180 nlinfo("ACK-ADD: CYCLIC. counter=%d. size=%d.", entry.Counter, _AckExecuteCycleList.size());
2181 #endif
2183 else
2185 entry.Counter= _PhraseNextExecuteCounter;
2186 entry.MemoryLine= _CurrentExecuteLineNext;
2187 entry.MemorySlot= _CurrentExecuteSlotNext;
2188 entry.PhraseId= _CurrentExecutePhraseIdNext;
2189 _AckExecuteNextList.push_back(entry);
2190 #ifdef PHRASE_DEBUG_VERBOSE
2191 nlinfo("ACK-ADD: NEXT. counter=%d. size=%d.", entry.Counter, _AckExecuteNextList.size());
2192 #endif
2195 // avoid any problems if server is too laggy. flush the list
2196 while(_AckExecuteCycleList.size()>PHRASE_EXECUTE_COUNTER_MASK+1)
2197 _AckExecuteCycleList.pop_front();
2198 while(_AckExecuteNextList.size()>PHRASE_EXECUTE_COUNTER_MASK+1)
2199 _AckExecuteNextList.pop_front();
2203 // ***************************************************************************
2204 void CSPhraseManager::resetAckExecuteList(bool cyclic)
2206 if(cyclic)
2208 _AckExecuteCycleList.clear();
2209 #ifdef PHRASE_DEBUG_VERBOSE
2210 nlinfo("ACK-RESET: CYCLIC.");
2211 #endif
2213 else
2215 _AckExecuteNextList.clear();
2216 #ifdef PHRASE_DEBUG_VERBOSE
2217 nlinfo("ACK-RESET: NEXT.");
2218 #endif
2223 // ***************************************************************************
2224 void CSPhraseManager::receiveAckExecuteFromServer(bool cyclic, uint counterValue, bool ok)
2226 // get the approriated list
2227 TAckExecuteList &ackList= cyclic?_AckExecuteCycleList:_AckExecuteNextList;
2229 // **** if it's a validation
2230 if(ok)
2232 // try to find the element in list
2233 TAckExecuteList::iterator it= ackList.begin();
2234 for(;it!=ackList.end();it++)
2236 if(it->Counter==counterValue)
2238 // found, mark
2239 it->State= CAckExecuteEntry::OK;
2240 // erase all elements before it
2241 ackList.erase(ackList.begin(), it);
2242 break;
2245 // NB: if not found, noop (NB: possible for instance if msg arrives after DB update, or if order
2246 // of ACK is not good)
2247 #ifdef PHRASE_DEBUG_VERBOSE
2248 nlinfo("ACK-RECEIVE-OK: %s. counter=%d. size=%d", cyclic?"CYCLIC":"NEXT", counterValue, ackList.size());
2249 #endif
2251 // **** server cancelation
2252 else
2254 // try to find the element in list
2255 bool found= false;
2256 TAckExecuteList::iterator it= ackList.begin();
2257 for(;it!=ackList.end();it++)
2259 if(it->Counter==counterValue)
2261 // found, mark as KO
2262 it->State= CAckExecuteEntry::KO;
2263 found= true;
2264 break;
2267 // NB: if not found, noop (NB: possible for instance if msg arrives after DB update, or if order
2268 // of ACK is not good)
2271 // if element succesfuly marked, must find which action we have to fall back
2272 if(found)
2274 // find the first element in list (start from end) that is not in KO state (ie WaitAck or OK)
2275 bool foundFallBack= false;
2276 CAckExecuteEntry fallBack;
2277 TAckExecuteList::reverse_iterator it= ackList.rbegin();
2278 for(;it!=ackList.rend();it++)
2280 if(it->State!=CAckExecuteEntry::KO)
2282 foundFallBack= true;
2283 fallBack= *it;
2284 break;
2288 // if no fallback found (the list contains only KO)
2289 if(!foundFallBack)
2291 // setup an empty slot
2292 fallBack.MemoryLine= -1;
2293 fallBack.MemorySlot= -1;
2294 fallBack.PhraseId= 0;
2297 // replace the Next or Cycle action view, with this fallback slot.
2298 if(cyclic)
2300 _CurrentExecuteLineCycle= fallBack.MemoryLine;
2301 _CurrentExecuteSlotCycle= fallBack.MemorySlot;
2302 _CurrentExecutePhraseIdCycle= fallBack.PhraseId;
2304 else
2306 _CurrentExecuteLineNext= fallBack.MemoryLine;
2307 _CurrentExecuteSlotNext= fallBack.MemorySlot;
2308 _CurrentExecutePhraseIdNext= fallBack.PhraseId;
2311 // update views
2312 updateExecutionDisplay();
2315 #ifdef PHRASE_DEBUG_VERBOSE
2316 nlinfo("ACK-RECEIVE-KO: %s. counter=%d. size=%d", cyclic?"CYCLIC":"NEXT", counterValue, ackList.size());
2317 #endif
2322 // ***************************************************************************
2323 /** Called when the database counter change
2325 class CHandlerPhraseCounterUpdate : public IActionHandler
2327 public:
2328 virtual void execute (CCtrlBase * /* pCaller */, const string &Params)
2330 CSPhraseManager *pPM = CSPhraseManager::getInstance();
2332 // Cyclic Cast?
2333 bool cyclic;
2334 cyclic= getParam(Params, "cyclic") == "1" ;
2336 if(cyclic)
2338 // if synchronized
2339 if(pPM->isPhraseCycleExecuteCounterSync())
2341 // reset the CycleAckList
2342 pPM->resetAckExecuteList(true);
2344 // reset slot played.
2345 pPM->_CurrentExecuteLineCycle= -1;
2346 pPM->_CurrentExecuteSlotCycle= -1;
2347 pPM->_CurrentExecutePhraseIdCycle= 0;
2349 // update views
2350 pPM->updateExecutionDisplay();
2353 else
2355 // if synchronized
2356 if(pPM->isPhraseNextExecuteCounterSync())
2358 // reset the NextAckList
2359 pPM->resetAckExecuteList(false);
2361 // reset slot played.
2362 pPM->_CurrentExecuteLineNext= -1;
2363 pPM->_CurrentExecuteSlotNext= -1;
2364 pPM->_CurrentExecutePhraseIdNext= 0;
2366 // update views
2367 pPM->updateExecutionDisplay();
2372 REGISTER_ACTION_HANDLER( CHandlerPhraseCounterUpdate, "phrase_counter_update");
2375 // ***************************************************************************
2376 class CHandlerPhraseDebugClient : public IActionHandler
2378 public:
2379 virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */)
2381 if(ClientCfg.Local)
2383 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2384 CSPhraseManager *pPM = CSPhraseManager::getInstance();
2386 // simulate server response for Action
2387 if(T1>pPM->_PhraseDebugEndNextAction)
2389 // copy counter
2390 NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_NEXT)->setValue32(pPM->_PhraseNextExecuteCounter);
2392 if(T1>pPM->_PhraseDebugEndCyclicAction)
2394 // copy counter
2395 NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_CYCLE)->setValue32(pPM->_PhraseCycleExecuteCounter);
2398 sint64 st= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:CURRENT_SERVER_TICK")->getValue64();
2399 sint64 stEnd= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TEND")->getValue64();
2400 if(stEnd && st>stEnd+20)
2401 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TEND")->setValue64(0);
2405 REGISTER_ACTION_HANDLER( CHandlerPhraseDebugClient, "phrase_debug_client");
2408 // ***************************************************************************
2409 void CSPhraseManager::setEquipInvalidation(sint64 serverTick, sint invalidTickTime)
2411 // if the item takes no time to invalidate, no-op!
2412 if(invalidTickTime<0)
2414 return;
2417 sint64 tempTick = serverTick + invalidTickTime;
2418 if(tempTick==0)
2420 _EquipInvalidationEnd = 0;
2422 else
2424 _EquipInvalidationEnd= max(_EquipInvalidationEnd, tempTick);
2428 // ***************************************************************************
2429 void CSPhraseManager::updateEquipInvalidation(sint64 serverTick)
2431 bool precState= isExecutionInvalidCauseEquip();
2433 // set new server tick
2434 _CurrentServerTick= serverTick;
2436 // if the state has changed, must update the ctrl states!
2437 if(precState != isExecutionInvalidCauseEquip())
2439 updateAllMemoryCtrlState();
2443 // ***************************************************************************
2444 void CSPhraseManager::updateAllActionRegen()
2446 if (_RegenTickRangeTouched)
2448 TTicks startTime = CTime::getPerformanceTime();
2449 updateAllMemoryCtrlRegenTickRange();
2450 _RegenTickRangeTouched = false;
2451 TTicks endTime = CTime::getPerformanceTime();
2452 //nldebug("***** %d ms for CSPhraseManager::updateAllActionRegen", (int) (1000 * CTime::ticksToSecond(endTime - startTime)));
2456 // ***************************************************************************
2457 bool CSPhraseManager::isExecutionInvalidCauseEquip() const
2459 return _EquipInvalidationEnd > _CurrentServerTick;
2462 // ***************************************************************************
2463 void CSPhraseManager::sendDeleteToServer(uint32 slot)
2465 if(!ClientCfg.Local)
2467 CBitMemStream out;
2468 if(GenericMsgHeaderMngr.pushNameToStream("PHRASE:DELETE", out))
2470 uint16 slotId= (uint16)slot;
2471 out.serial(slotId);
2472 NetMngr.push(out);
2474 else
2475 nlwarning(" unknown message name 'PHRASE:DELETE");
2479 // ***************************************************************************
2480 void CSPhraseManager::sendLearnToServer(uint32 slot)
2482 CSPhraseCom phrase= getPhrase(slot);
2483 if(phrase.empty())
2484 return;
2486 if(!ClientCfg.Local)
2488 CBitMemStream out;
2489 if(GenericMsgHeaderMngr.pushNameToStream("PHRASE:LEARN", out))
2491 uint16 slotId= (uint16)slot;
2492 out.serial(slotId);
2493 out.serial(phrase);
2494 NetMngr.push(out);
2496 else
2497 nlwarning(" unknown message name 'PHRASE:LEARN");
2501 // ***************************************************************************
2502 void CSPhraseManager::fullDelete(uint32 slot)
2504 // auto-forget all before.
2505 forgetAllThatUsePhrase(slot);
2507 // erase it.
2508 erasePhrase(slot);
2510 // send the erase to server
2511 sendDeleteToServer(slot);
2514 // ***************************************************************************
2515 uint CSPhraseManager::countAllThatUsePhrase(uint32 slot)
2517 if(slot==0)
2518 return 0;
2520 // for all memory slot
2521 uint count= 0;
2522 for(uint i=0;i<_Memories.size();i++)
2524 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
2526 if( _Memories[i].Slot[j].isPhrase() &&
2527 _Memories[i].Slot[j].Id==slot )
2529 count++;
2534 return count;
2537 // ***************************************************************************
2538 void CSPhraseManager::forgetAllThatUsePhrase(uint32 slot, bool sendMsgOnly)
2540 if(slot==0)
2541 return;
2543 bool someForgetedOnClient= false;
2545 // for all memory slot
2546 for(uint i=0;i<_Memories.size();i++)
2548 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
2550 if( _Memories[i].Slot[j].isPhrase() &&
2551 _Memories[i].Slot[j].Id==slot )
2553 // forget the phrase localy, if wanted
2554 if(!sendMsgOnly)
2556 forgetPhrase(i, j);
2557 someForgetedOnClient= true;
2560 // forget the phrase to server
2561 // TODO_DAVID_PHRASE_CLEAN: remove JUST the line below
2562 sendForgetToServer(i, j);
2567 // easy: update all memory ctrl states, if some forgeted on client
2568 if(someForgetedOnClient)
2569 updateAllMemoryCtrlState();
2572 // ***************************************************************************
2573 void CSPhraseManager::rememorizeAllThatUsePhrase(uint32 slot)
2575 bool someMemorized= false;
2577 // for all memory slot
2578 for(uint i=0;i<_Memories.size();i++)
2580 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
2582 if( _Memories[i].Slot[j].isPhrase() &&
2583 _Memories[i].Slot[j].Id==slot )
2585 // re-memorize the phrase, need server only
2586 // TODO_DAVID_PHRASE_CLEAN: remove JUST the line below
2587 sendMemorizeToServer(i, j, slot);
2589 // for updating client gray states
2590 someMemorized= true;
2595 // easy: update all memory ctrl states, if some re-learned
2596 if(someMemorized)
2597 updateAllMemoryCtrlState();
2601 // ***************************************************************************
2602 CSheetId getRightHandItem()
2604 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2606 CSheetId item;
2607 // get the RightHand bag index
2608 sint32 itemSlot= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:HAND:0:INDEX_IN_BAG")->getValue32();
2609 // if something in hand
2610 if(itemSlot>0)
2612 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot-1) +":SHEET", false);
2613 if(node)
2614 item= node->getValue32();
2617 return item;
2620 // ***************************************************************************
2621 // retrieve the current Skill of the Item in RightHand
2622 SKILLS::ESkills getRightHandItemSkill()
2624 SKILLS::ESkills itemSkill= SKILLS::unknown;
2626 CSheetId item= getRightHandItem();
2628 if(item.asInt()!=0)
2630 // get the ItemSheet.
2631 CItemSheet *itemSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(item));
2632 if(itemSheet)
2633 itemSkill= itemSheet->getRequiredSkill();
2635 else
2637 // Empty Hand => Hand skill
2638 itemSkill= SKILLS::SFMCAH;
2640 return itemSkill;
2643 // ***************************************************************************
2644 // retrieve the current CraftToolType in RightHand
2645 TOOL_TYPE::TCraftingToolType getRightHandCraftToolType()
2647 TOOL_TYPE::TCraftingToolType toolType= TOOL_TYPE::Unknown;
2649 CSheetId item= getRightHandItem();
2651 if(item.asInt()!=0)
2653 // get the ItemSheet.
2654 CItemSheet *itemSheet= dynamic_cast<CItemSheet*>(SheetMngr.get(item));
2655 if(itemSheet && itemSheet->Family == ITEMFAMILY::CRAFTING_TOOL)
2656 toolType= itemSheet->Tool.CraftingToolType;
2659 return toolType;
2662 // ***************************************************************************
2663 uint32 getRightHandEffectiveLevel()
2665 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2666 CSkillManager *pSM= CSkillManager::getInstance();
2668 uint32 effectiveLevel= 0;
2670 // **** get the right hand item skill value
2671 SKILLS::ESkills rhSkill= getRightHandItemSkill();
2672 effectiveLevel= pSM->getSkillValueMaxBranch(rhSkill);
2674 // **** get the right hand item 'required skill'
2675 // get the RightHand bag index
2676 sint32 itemSlot= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:HAND:0:INDEX_IN_BAG")->getValue32();
2677 // if something in hand
2678 if(itemSlot>0)
2680 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot-1) +":QUALITY", false);
2681 if(node)
2682 // if the right hand item quality is less than our skill value, take it....
2683 effectiveLevel= min(effectiveLevel, (uint32)node->getValue32());
2685 // else awlays take the bare hand skill level (above)
2687 return effectiveLevel;
2690 // ***************************************************************************
2691 static sint getRightHandEnchantValue()
2693 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2695 sint ret= 0;
2696 // get the RightHand bag index
2697 sint32 itemSlot= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:HAND:0:INDEX_IN_BAG")->getValue32();
2698 // if something in hand
2699 if(itemSlot>0)
2701 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot-1) +":ENCHANT", false);
2702 if(node)
2703 ret= node->getValue32();
2706 return ret;
2709 // ***************************************************************************
2710 void CSPhraseManager::updateMemoryCtrlRegenTickRange(uint memorySlot, CDBCtrlSheet *ctrl)
2713 sint32 memoryLine;
2714 if (ctrl->isShortCut())
2715 memoryLine = getSelectedMemoryLineDB();
2716 else
2717 memoryLine = getSelectedMemoryAltLineDB();
2718 if (memoryLine < 0)
2719 return;
2720 sint32 phraseId= getMemorizedPhrase(memoryLine, memorySlot);
2722 if(phraseId)
2724 const CSPhraseCom &phrase = getPhrase(phraseId);
2725 CTickRange rtr = getRegenTickRange(phrase);
2726 if ((_RegenPhrases.find(phraseId) != _RegenPhrases.end()) && rtr.isEmpty() && !ctrl->getRegenTickRange().isEmpty())
2728 ctrl->startNotifyAnim(); // anim to state that the phraseis available again
2729 _RegenPhrases.erase(_RegenPhrases.find(phraseId));
2731 if (!rtr.isEmpty())
2732 _RegenPhrases.insert(phraseId);
2733 ctrl->setRegenTickRange(rtr);
2737 // ***************************************************************************
2738 uint64 CSPhraseManager::getPhraseRequiredFlags(const CSPhraseCom &phrase)
2740 CSBrickManager *pBM= CSBrickManager::getInstance();
2741 uint64 phraseFlags = 0;
2742 for(uint i=0;i<phrase.Bricks.size();i++)
2744 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
2745 if(brick)
2746 phraseFlags|= brick->BrickRequiredFlags;
2748 return phraseFlags;
2751 // ***************************************************************************
2752 CTickRange CSPhraseManager::getRegenTickRange(const CSPhraseCom &phrase) const
2754 CTickRange tickRange;
2755 if(!phrase.empty())
2757 CSBrickManager *pBM= CSBrickManager::getInstance();
2758 // Get the RootBrick of the Phrase
2759 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[0]);
2760 if(brick)
2762 if (brick->isSpecialPower())
2764 uint64 phraseFlags = getPhraseRequiredFlags(phrase);
2765 for(uint b = 0; b < 64; ++b)
2767 if (phraseFlags & (uint64(1) << b))
2769 CTickRange newTR = getRegenTickRange(b);
2770 if (!newTR.isEmpty())
2772 if (tickRange.StartTick == tickRange.EndTick)
2774 tickRange = newTR;
2776 else
2778 tickRange.extend(newTR);
2786 return tickRange;
2790 // ***************************************************************************
2791 NLMISC::TGameCycle CSPhraseManager::getPowerDisableTime(const CSPhraseCom &phrase) const
2793 float regenTime = 0;
2794 if(!phrase.empty())
2796 CSBrickManager *pBM= CSBrickManager::getInstance();
2797 for (uint brickIndex = 0; brickIndex < phrase.Bricks.size(); ++brickIndex)
2799 // Get the RootBrick of the Phrase
2800 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[brickIndex]);
2801 if(brick)
2803 for (uint prop = 0; prop < brick->Properties.size(); ++prop)
2805 std::string::size_type endPos = brick->Properties[prop].Text.find(":");
2806 if (endPos != std::string::npos)
2808 std::string propName = brick->Properties[prop].Text.substr(0, endPos);
2809 if (nlstricmp(propName, "SP_RECAST_TIME") == 0)
2811 regenTime = std::max(regenTime, brick->Properties[prop].Value);
2818 return (NLMISC::TGameCycle) (regenTime * 10.f);
2822 // ***************************************************************************
2823 NLMISC::TGameCycle CSPhraseManager::getRegenTime(const CSPhraseCom &phrase) const
2825 CTickRange tickRange = getRegenTickRange(phrase);
2826 sint left = LastGameCycle - tickRange.StartTick;
2827 return (uint) std::max(0, left);
2830 // ***************************************************************************
2831 NLMISC::TGameCycle CSPhraseManager::getTotalRegenTime(const CSPhraseCom &phrase) const
2833 CTickRange tickRange = getRegenTickRange(phrase);
2834 return (tickRange.EndTick - tickRange.StartTick);
2838 // ***************************************************************************
2839 void CSPhraseManager::updateMemoryCtrlState(uint memorySlot, CDBCtrlSheet *ctrl, SKILLS::ESkills itemSkill)
2842 Update Gray State of Memory Ctrls.
2843 Can Execute only if skill of the phrase and skill of the selected Weapon are compatible.
2844 Eg: can execute a "FyrosAxe" phrase on a FyrosAxe Weapon (skills on same branch)
2845 can execute a "Combat" phrase on a FyrosAxe Weapon (skills on same branch)
2846 can't execute a "MatisAxe" phrase on a FyrosAxe Weapon (skills not on same branch)
2847 Additionaly, a Magic Phrase is always executable
2850 CInterfaceManager *pIM = CInterfaceManager::getInstance();
2851 CSBrickManager *pBM = CSBrickManager::getInstance();
2852 CSkillManager *pSM = CSkillManager::getInstance();
2854 uint memoryLine;
2855 // get the slot info
2856 if (ctrl->isShortCut()) // No memoryLine defined
2857 memoryLine= getSelectedMemoryLineDB();
2858 else
2859 memoryLine= getSelectedMemoryAltLineDB();
2860 bool newIsMacro= isMemorizedMacro(memoryLine, memorySlot);
2861 sint32 macroId= getMemorizedMacro(memoryLine, memorySlot);
2862 sint32 phraseId= getMemorizedPhrase(memoryLine, memorySlot);
2863 CMemorySlot *memSlot= NULL;
2864 if(memoryLine<_Memories.size() && memorySlot<PHRASE_MAX_MEMORY_SLOT)
2865 memSlot= &_Memories[memoryLine].Slot[memorySlot];
2867 // update the ctrl
2868 if(ctrl && (ctrl->isSPhraseId() || ctrl->isMacro()) )
2870 // if the new ctrl is a phrase
2871 if(!newIsMacro)
2873 // **** if ctrl is macro, change type
2874 if(ctrl->isMacro())
2876 ctrl->setType(CCtrlSheetInfo::SheetType_SPhraseId);
2877 ctrl->setListMenuRight(PhraseMemoryPhraseMenu);
2878 ctrl->setActionOnLeftClick(PhraseMemoryPhraseAction);
2881 // **** get the related ctrl skill
2882 SKILLS::ESkills phraseRootSkill= SKILLS::unknown;
2883 bool isGlobalMemPhrase= false;
2884 bool isFaberPhrase= false;
2885 bool isHarvestPhrase= false;
2886 bool isCombatPhrase= false;
2887 bool isProcEnchantmentPhrase= false;
2888 bool isSpecialPowerPhrase= false;
2889 if(phraseId)
2891 const CSPhraseCom &phrase= getPhrase(phraseId);
2892 phraseRootSkill= getPhraseRootSkill(phrase.Bricks);
2893 if(!phrase.empty())
2895 // Get the RootBrick of the Phrase
2896 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[0]);
2897 if(brick)
2899 // Magic or SpecialPower Root?
2900 isGlobalMemPhrase= brick->isMagic() || brick->isSpecialPower();
2901 isSpecialPowerPhrase= brick->isSpecialPower();
2902 isFaberPhrase= brick->isFaber();
2903 isHarvestPhrase= brick->isHarvest();
2904 isCombatPhrase= brick->isCombat();
2905 isProcEnchantmentPhrase= brick->isProcEnchantment();
2911 // **** gray ctrl if not compatibles
2912 bool valid= false;
2913 if( isGlobalMemPhrase || pSM->areSkillOnSameBranch(phraseRootSkill, itemSkill) )
2914 valid= true;
2916 // *** Combat: the item must be compatible with the phrase
2917 if( valid && isCombatPhrase && !skillCompatibleWithCombatPhrase(itemSkill, getPhrase(phraseId).Bricks) )
2918 valid= false;
2920 // *** Power: item enchant.
2921 if( valid && isSpecialPowerPhrase && !skillCompatibleWithSpecialPowerPhrase(itemSkill, getPhrase(phraseId).Bricks) )
2922 valid= false;
2924 // *** Harvet : not in indoor
2925 if (isHarvestPhrase && UserEntity->indoor())
2926 valid= false;
2928 // **** Faber special: valid only if good ToolType
2929 if(valid && itemSkill == SKILLS::SC && isFaberPhrase)
2931 valid= false;
2933 TOOL_TYPE::TCraftingToolType cttHand= getRightHandCraftToolType();
2934 // Get The FaberPlan
2935 const CSPhraseCom &phrase= getPhrase(phraseId);
2936 if(!phrase.empty())
2938 // Ok if the item built can be build with the tool in hand
2939 valid= pBM->getSabrinaCom().getPhraseFaberPlanToolType(phrase.Bricks) == cttHand;
2943 // **** Proc Enchantment: valid only if the item in right hand is enchanted
2944 if( isProcEnchantmentPhrase )
2946 valid= false;
2947 sint enchantValue= getRightHandEnchantValue();
2948 // dec because 0 == not enchanted, and 1 == enchanted but no more charge
2949 enchantValue--;
2950 // if some charge left
2951 if(enchantValue>=1)
2952 valid= true;
2955 // *** check if need an item in hands
2956 if( valid && isCombatPhrase && !phraseUsableWithEmptyHands( getPhrase(phraseId).Bricks) )
2958 if( getRightHandItem() == CSheetId::Unknown )
2959 valid= false;
2963 // **** special: valid only according to BRICK_TICK_RANGE dbProp.
2964 if(valid)
2966 uint64 currentFlags;
2967 if (isSpecialPowerPhrase)
2969 currentFlags = ~(uint64(0));
2970 //Power Flag
2971 for (uint powerIndex = 32; powerIndex < 64; ++powerIndex)
2973 if (!getRegenTickRange(powerIndex).isEmpty())
2975 currentFlags &= ~(uint64(1) << powerIndex);
2979 else
2981 currentFlags = uint64(0);
2982 //CombatEvent Flag
2983 for (uint powerIndex = 0; powerIndex < 32; ++powerIndex)
2985 if (!getRegenTickRange(powerIndex).isEmpty())
2987 currentFlags |= uint64(1) << powerIndex;
2992 // Build the phrase required bricks flags
2993 const CSPhraseCom &phrase= getPhrase(phraseId);
2994 uint64 phraseFlags = getPhraseRequiredFlags(phrase);
2995 // if required flags are not present in database, invalid
2996 if( (currentFlags & phraseFlags) != phraseFlags )
2998 valid= false;
3005 // **** valid only if the user is not equiping himself
3006 if(isExecutionInvalidCauseEquip())
3008 valid= false;
3011 // Check if the temporary inventory is opened
3012 if(isExecutionInvalidCauseTempInv())
3014 valid= false;
3017 // **** update state
3018 ctrl->setGrayed(!valid);
3020 else
3022 bool neadReadMacro= false;
3023 nlassert(memSlot);
3025 // **** if ctrl is macro, change type
3026 if(ctrl->isSPhraseId())
3028 ctrl->setType(CCtrlSheetInfo::SheetType_Macro);
3029 ctrl->setListMenuRight(PhraseMemoryMacroMenu);
3030 ctrl->setActionOnLeftClick(PhraseMemoryMacroAction);
3031 neadReadMacro= true;
3034 // macro changed?
3035 if(ctrl->getMacroId() != macroId)
3036 neadReadMacro= true;
3038 // slot dirty?
3039 if(memSlot->IsMacroVisualDirty)
3041 neadReadMacro= true;
3042 // clean
3043 memSlot->IsMacroVisualDirty= false;
3046 // change macro
3047 if(neadReadMacro)
3049 CMacroCmdManager *pMM= CMacroCmdManager::getInstance();
3050 const CMacroCmd *macroCmd= pMM->getMacroFromMacroID(macroId);
3051 if(macroCmd)
3052 ctrl->readFromMacro(*macroCmd);
3055 // **** set gray state
3056 ctrl->setGrayed(false);
3059 _RegenTickRangeTouched = true;
3063 // ***************************************************************************
3064 void CSPhraseManager::updateAllMemoryCtrlState()
3066 TTicks startTime = CTime::getPerformanceTime();
3067 // get the item skill
3068 SKILLS::ESkills itemSkill= getRightHandItemSkill();
3070 // Update All Memory Ctrls
3071 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3072 // For All the selected Memory Ctrls.
3073 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
3075 // Get the ctrl
3076 CDBCtrlSheet *ctrl= dynamic_cast<CDBCtrlSheet*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryCtrlBase + toString(i)) );
3077 if(ctrl)
3079 // update the valid state.
3080 updateMemoryCtrlState(i, ctrl, itemSkill);
3082 CDBCtrlSheet *ctrlAlt= dynamic_cast<CDBCtrlSheet*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryAltCtrlBase + toString(i)) );
3083 if(ctrlAlt)
3084 updateMemoryCtrlState(i, ctrlAlt, itemSkill);
3086 TTicks endTime = CTime::getPerformanceTime();
3087 //nldebug("***** %d ms for CSPhraseManager::updateAllMemoryCtrlState", (int) (1000 * CTime::ticksToSecond(endTime - startTime)));
3090 // ***************************************************************************
3091 void CSPhraseManager::updateAllMemoryCtrlRegenTickRange()
3093 for(uint i=0;i<PHRASE_MAX_MEMORY_SLOT;i++)
3095 updateMemoryCtrlRegenTickRange(i);
3100 // ***************************************************************************
3101 CDBCtrlSheet *CSPhraseManager::getMemorySlotCtrl(uint memorySlot)
3103 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3104 return NULL;
3106 // Get the ctrl
3107 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3108 return dynamic_cast<CDBCtrlSheet*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryCtrlBase + toString(memorySlot)));
3111 // ***************************************************************************
3112 CDBCtrlSheet *CSPhraseManager::getMemoryAltSlotCtrl(uint memorySlot)
3114 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3115 return NULL;
3117 // Get the ctrl
3118 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3119 return dynamic_cast<CDBCtrlSheet*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryAltCtrlBase + toString(memorySlot)));
3122 // ***************************************************************************
3123 void CSPhraseManager::updateMemoryCtrlState(uint memorySlot)
3125 CDBCtrlSheet *ctrl= getMemorySlotCtrl(memorySlot);
3126 CDBCtrlSheet *ctrlAlt= getMemoryAltSlotCtrl(memorySlot);
3127 if(ctrl)
3129 // update the valid state.
3130 updateMemoryCtrlState(memorySlot, ctrl, getRightHandItemSkill());
3132 if(ctrlAlt)
3134 // update the valid state.
3135 updateMemoryCtrlState(memorySlot, ctrlAlt, getRightHandItemSkill());
3139 // ***************************************************************************
3140 void CSPhraseManager::updateMemoryCtrlRegenTickRange(uint memorySlot)
3142 CDBCtrlSheet *ctrl= getMemorySlotCtrl(memorySlot);
3143 CDBCtrlSheet *ctrlAlt= getMemoryAltSlotCtrl(memorySlot);
3144 if(ctrl)
3146 // update the valid state.
3147 updateMemoryCtrlRegenTickRange(memorySlot, ctrl);
3149 if(ctrlAlt)
3151 // update the valid state.
3152 updateMemoryCtrlRegenTickRange(memorySlot, ctrlAlt);
3156 // ***************************************************************************
3157 SKILLS::ESkills CSPhraseManager::getPhraseRootSkill(const std::vector<NLMISC::CSheetId> &phraseBricks) const
3159 if(phraseBricks.empty())
3160 return SKILLS::unknown;
3162 CSBrickManager *pBM= CSBrickManager::getInstance();
3163 CSBrickSheet *rootBrick= pBM->getBrick(phraseBricks[0]);
3164 if(!rootBrick)
3165 return SKILLS::unknown;
3166 else
3167 return rootBrick->getSkill();
3170 // ***************************************************************************
3171 bool CSPhraseManager::skillCompatibleWithCombatPhrase(SKILLS::ESkills compareSkill, const std::vector<NLMISC::CSheetId> &phraseBricks) const
3173 // special case for hand to hand due to possible brick restriction
3174 if( compareSkill == SKILLS::SFMCAHM )
3176 if( !phraseUsableWithEmptyHands(phraseBricks) )
3178 return false;
3182 CSkillManager *pSM= CSkillManager::getInstance();
3184 // **** Build the "phrase skill compatible" formula.
3185 CReqSkillFormula phraseFormula= buildCombatPhraseSkillFormula(phraseBricks);
3187 // **** test if one of the ored skill match
3188 std::list<CReqSkillFormula::CSkillValueAnd>::iterator it(phraseFormula.OrSkills.begin()),
3189 end(phraseFormula.OrSkills.end());
3190 for(;it!=end;it++)
3192 CReqSkillFormula::CSkillValueAnd skillAnd= *it;
3193 // if the and is not a single value, then no chance to match (eg: SFR&SFM won't match any compareSkill)
3194 if(skillAnd.AndSkills.size()==1)
3196 // ok if the skill to compare is more restrictive (or equal) to this skill
3197 if(pSM->isSkillAncestor(skillAnd.AndSkills.begin()->Skill, compareSkill))
3198 return true;
3202 return false;
3205 // ***************************************************************************
3206 bool CSPhraseManager::skillCompatibleWithSpecialPowerPhrase(SKILLS::ESkills compareSkill, const std::vector<NLMISC::CSheetId> &phraseBricks) const
3208 CSkillManager *pSM= CSkillManager::getInstance();
3210 bool hasEnchantWeapon = false;
3211 vector<NLMISC::CSheetId>::const_iterator itBrick, itBrickEnd = phraseBricks.end();
3212 for (itBrick=phraseBricks.begin(); itBrick!=itBrickEnd; ++itBrick)
3214 NLMISC::CSheetId const& brick = *itBrick;
3215 if (brick==_EnchantWeaponMainBrick)
3216 hasEnchantWeapon = true;
3219 // If phrase enchants a weapon
3220 if (hasEnchantWeapon)
3222 // check we have a melee weapon in hand
3223 if (pSM->isSkillAncestor(SKILLS::SFM, compareSkill) && compareSkill!=SKILLS::SFMCAH)
3224 return true;
3225 else
3226 return false;
3229 return true;
3233 // ***************************************************************************
3234 bool CSPhraseManager::phraseUsableWithEmptyHands(const std::vector<NLMISC::CSheetId> &phraseBricks) const
3236 vector<NLMISC::CSheetId>::const_iterator itBrick, itBrickEnd = phraseBricks.end();
3237 for (itBrick=phraseBricks.begin(); itBrick!=itBrickEnd; ++itBrick)
3239 CSBrickSheet *brick= CSBrickManager::getInstance()->getBrick(*itBrick);
3240 if(brick)
3242 if( !brick->UsableWithEmptyHands )
3243 return false;
3246 return true;
3250 // ***************************************************************************
3251 // ***************************************************************************
3252 // MACROS
3253 // ***************************************************************************
3254 // ***************************************************************************
3256 // ***************************************************************************
3257 void CSPhraseManager::memorizeMacro(uint32 memoryLine, uint32 memorySlot, uint32 macroId)
3259 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3260 return;
3262 // enlarge memory if needed
3263 if(memoryLine>=_Memories.size())
3264 _Memories.resize(memoryLine+1);
3266 // if the slot is a phrase
3267 if(_Memories[memoryLine].Slot[memorySlot].isPhrase())
3269 // forget old phrase slot, both client and server
3270 forgetPhrase(memoryLine, memorySlot);
3271 sendForgetToServer(memoryLine, memorySlot);
3274 // update memory
3275 _Memories[memoryLine].Slot[memorySlot].IsMacro= true;
3276 _Memories[memoryLine].Slot[memorySlot].Id= macroId;
3278 // must update DB?
3279 if((sint32)memoryLine==_SelectedMemoryDB)
3281 // update the DB
3282 updateMemoryDBSlot(memorySlot);
3283 // update the ctrl state
3284 updateMemoryCtrlState(memorySlot);
3288 // ***************************************************************************
3289 void CSPhraseManager::forgetMacro(uint32 memoryLine, uint32 memorySlot)
3291 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3292 return;
3293 // if the slot eihter don't exist, abort
3294 if(memoryLine>=_Memories.size())
3295 return;
3296 // if the slot is not a macro
3297 if(!_Memories[memoryLine].Slot[memorySlot].isMacro())
3298 return;
3300 // update memory. NB: revert to Phrase mode is important
3301 _Memories[memoryLine].Slot[memorySlot].IsMacro= false;
3302 _Memories[memoryLine].Slot[memorySlot].Id= 0;
3304 // must update DB?
3305 if((sint32)memoryLine==_SelectedMemoryDB)
3307 // update the db
3308 updateMemoryDBSlot(memorySlot);
3309 // update the ctrl state
3310 updateMemoryCtrlState(memorySlot);
3314 // ***************************************************************************
3315 uint32 CSPhraseManager::getMemorizedMacro(uint32 memoryLine, uint32 memorySlot) const
3317 if(memoryLine>=_Memories.size())
3318 return 0;
3319 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3320 return 0;
3321 if(!_Memories[memoryLine].Slot[memorySlot].isMacro())
3322 return 0;
3324 return _Memories[memoryLine].Slot[memorySlot].Id;
3327 // ***************************************************************************
3328 bool CSPhraseManager::isMemorizedMacro(uint32 memoryLine, uint32 memorySlot) const
3330 if(memoryLine>=_Memories.size())
3331 return 0;
3332 if(memorySlot>=PHRASE_MAX_MEMORY_SLOT)
3333 return 0;
3335 return _Memories[memoryLine].Slot[memorySlot].isMacro();
3338 // ***************************************************************************
3339 void CSPhraseManager::updateMacroShortcuts(sint32 macroId)
3341 // dirt any macro visual
3342 for(uint i=0;i<_Memories.size();i++)
3344 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
3346 CMemorySlot &memSlot= _Memories[i].Slot[j];
3347 if(memSlot.isMacro() && memSlot.Id==(uint32)macroId)
3348 memSlot.IsMacroVisualDirty= true;
3352 // update the visual
3353 updateAllMemoryCtrlState();
3356 // ***************************************************************************
3357 void CSPhraseManager::deleteMacroShortcuts(sint32 macroId)
3359 // forget any macro that use this slot
3360 for(uint i=0;i<_Memories.size();i++)
3362 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
3364 CMemorySlot &memSlot= _Memories[i].Slot[j];
3365 if(memSlot.isMacro() && memSlot.Id==(uint32)macroId)
3366 forgetMacro(i, j);
3372 // ***************************************************************************
3373 struct CMacroMemSerialSlot
3375 uint8 MemoryLine;
3376 uint8 MemorySlot;
3377 uint32 MacroId;
3379 void serial(NLMISC::IStream &f)
3381 f.serial(MemoryLine);
3382 f.serial(MemorySlot);
3383 f.serial(MacroId);
3387 void CSPhraseManager::serialMacroMemory(NLMISC::IStream &f)
3389 // load
3390 if(f.isReading())
3392 CMacroCmdManager *pMM= CMacroCmdManager::getInstance();
3394 // read the list of slot
3395 vector<CMacroMemSerialSlot> macroList;
3396 f.serialCont(macroList);
3398 // for each one, test if the macro really exist, then set
3399 for(uint i=0;i<macroList.size();i++)
3401 CMacroMemSerialSlot slot= macroList[i];
3402 if(pMM->getMacroFromMacroID(slot.MacroId))
3403 memorizeMacro(slot.MemoryLine, slot.MemorySlot, slot.MacroId);
3406 // write
3407 else
3409 // create the list of macros that resides in the memory
3410 vector<CMacroMemSerialSlot> macroList;
3412 // for all mems, test if macro, then append
3413 for(uint i=0;i<_Memories.size();i++)
3415 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
3417 CMemorySlot &memSlot= _Memories[i].Slot[j];
3418 if(memSlot.isMacro())
3420 CMacroMemSerialSlot slot;
3421 slot.MemoryLine= i;
3422 slot.MemorySlot= j;
3423 slot.MacroId= memSlot.Id;
3424 macroList.push_back(slot);
3429 // then serial
3430 f.serialCont(macroList);
3435 // ***************************************************************************
3436 // ***************************************************************************
3437 // BOTCHAT
3438 // ***************************************************************************
3439 // ***************************************************************************
3442 // ***************************************************************************
3443 uint32 CSPhraseManager::getCurrentPhraseSheetPrice(uint32 phraseSheetId)
3445 CSBrickManager *pBM= CSBrickManager::getInstance();
3447 uint price= 0;
3449 // get the SPhraseSheet
3450 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(phraseSheetId)));
3451 if(phraseSheet)
3453 // Append the price of all bricks
3454 for(uint j=0;j<phraseSheet->Bricks.size();j++)
3456 // add the price only if the brick is not already learned
3457 if(!pBM->isBrickKnown(phraseSheet->Bricks[j]))
3459 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[j]);
3460 if(brick)
3462 price+= brick->SPCost;
3468 return price;
3471 // ***************************************************************************
3472 uint32 CSPhraseManager::getBasePhraseSheetPrice(uint32 phraseSheetId)
3474 CSBrickManager *pBM= CSBrickManager::getInstance();
3476 uint price= 0;
3478 // get the SPhraseSheet
3479 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(phraseSheetId)));
3480 if(phraseSheet)
3482 // Append the price of all bricks
3483 for(uint j=0;j<phraseSheet->Bricks.size();j++)
3485 // add the price either if the brick is not already learned
3486 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[j]);
3487 if(brick)
3489 price+= brick->SPCost;
3494 return price;
3497 // ***************************************************************************
3498 void CSPhraseManager::updateBotChatPhrasePrice(uint start, uint end)
3500 end= min(end, (uint)PHRASE_MAX_BOTCHAT_SLOT);
3501 start= min(start, end);
3503 // For all phrase to test
3504 for(uint i=start;i<end;i++)
3506 if(_BotChatPhraseSheetLeaves[i] && _BotChatPhrasePriceLeaves[i])
3508 uint price= getCurrentPhraseSheetPrice(_BotChatPhraseSheetLeaves[i]->getValue32());
3510 // set the price
3511 _BotChatPhrasePriceLeaves[i]->setValue32(price);
3517 // ***************************************************************************
3518 // ***************************************************************************
3519 // PROGRESSION
3520 // ***************************************************************************
3521 // ***************************************************************************
3524 // ***************************************************************************
3525 void CSPhraseManager::updatePhraseProgressionDB()
3527 // If DB not inited, no-op
3528 if(!_InitInGameDone)
3529 return;
3531 CSBrickManager *pBM= CSBrickManager::getInstance();
3533 // Fill All the progression db.
3534 uint numProgressFill= 0;
3535 if(_BookSkillFitler!=SKILLS::unknown)
3536 numProgressFill= (uint)_ProgressionPhrases[_BookSkillFitler].Phrases.size();
3537 uint i;
3538 uint progressIndex[NumProgressType];
3539 for(i=0;i<NumProgressType;i++)
3541 progressIndex[i]= 0;
3544 // for all phrases to fill
3545 for(i=0;i<numProgressFill;i++)
3547 CPhraseProgressInfo &ppi= _ProgressionPhrases[_BookSkillFitler].Phrases[i];
3549 // choose the progression where to write to
3550 uint progressType;
3551 if(isPhraseCastable(ppi.SheetId))
3552 progressType= ActionProgress;
3553 else
3554 progressType= UpgradeProgress;
3555 uint &dbIndex= progressIndex[progressType];
3558 // if no more place in db, skip this phrase
3559 if(dbIndex>=PHRASE_MAX_PROGRESSION_SLOT)
3561 continue;
3565 // is this phrase known?
3566 bool known= true;
3567 CSPhraseSheet *phrase= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(NLMISC::CSheetId(ppi.SheetId)));
3568 if(phrase)
3570 // if only one brick is not known, the phrase is not known
3571 for(uint j=0;j<phrase->Bricks.size();j++)
3573 if(!pBM->isBrickKnown(phrase->Bricks[j]))
3575 known= false;
3576 break;
3580 // if show, but only if full learnt, skip it if not fully learnt
3581 if (phrase->ShowInAPOnlyIfLearnt && !known)
3583 continue;
3588 // fill with the phrase id
3589 _ProgressionDbSheets[progressType][dbIndex]->setValue32(ppi.SheetId);
3590 // fill the level info
3591 _ProgressionDbLevels[progressType][dbIndex]->setValue32(ppi.Level);
3593 // if known, ungray
3594 if(known)
3595 _ProgressionDbLocks[progressType][dbIndex]->setValue32(0);
3596 else
3598 // just gray if all requirement are met in order to learn it
3599 if(phraseRequirementOk(ppi.SheetId))
3600 _ProgressionDbLocks[progressType][dbIndex]->setValue32(1);
3601 // else redify
3602 else
3603 _ProgressionDbLocks[progressType][dbIndex]->setValue32(2);
3606 // increment the db index
3607 dbIndex++;
3611 // for each progression type
3612 for(uint pt= 0;pt<NumProgressType;pt++)
3614 // reset old no more used to empty
3615 for(i=progressIndex[pt];i<_LastProgressionNumDbFill[pt];i++)
3617 _ProgressionDbSheets[pt][i]->setValue32(0);
3619 // update cache
3620 _LastProgressionNumDbFill[pt]= progressIndex[pt];
3625 // ***************************************************************************
3626 class CPhraseSortEntry
3628 public:
3629 CSPhraseManager::CPhraseProgressInfo ProgressInfo;
3630 // Key sort
3631 uint32 Section;
3632 bool Castable;
3633 uint32 Type;
3634 uint32 Icon;
3635 string Text;
3637 bool operator<(const CPhraseSortEntry &pse) const
3639 // sort by Section
3640 if(Section!=pse.Section)
3641 return Section<pse.Section;
3642 // take first castable (invert sens)
3643 if(Castable!=pse.Castable)
3644 return Castable>pse.Castable;
3645 // sort by type
3646 if(Type!=pse.Type)
3647 return Type<pse.Type;
3648 // sort by Icon (same type possible for effect bricks)
3649 if(Icon!=pse.Icon)
3650 return Icon<pse.Icon;
3651 // sort by text cost for same spell
3652 return Text<pse.Text;
3656 // ***************************************************************************
3657 void CSPhraseManager::computePhraseProgression()
3659 CSBrickManager *pBM= CSBrickManager::getInstance();
3660 CSkillManager *pSM= CSkillManager::getInstance();
3662 // progression
3663 sint skillReqLevel[SKILLS::NUM_SKILLS];
3664 std::vector<SKILLS::ESkills> skillsToInsert;
3665 skillsToInsert.reserve(SKILLS::NUM_SKILLS);
3666 memset(skillReqLevel, 0xFF, SKILLS::NUM_SKILLS*sizeof(sint));
3668 // **** For each Phrase Sheet, compute its related progression
3669 CSheetManager::TEntitySheetMap::const_iterator it;
3670 for(it=SheetMngr.getSheets().begin();it!=SheetMngr.getSheets().end();it++)
3672 if(it->second.EntitySheet->Type==CEntitySheet::SPHRASE)
3674 const CSPhraseSheet *phrase= safe_cast<const CSPhraseSheet*>(it->second.EntitySheet);
3676 #ifdef PHRASE_DEBUG_VERBOSE
3677 static uint pscount= 0;
3678 nlinfo("**** PROG PHRASE: %4d, %s", pscount++, phrase->Id.toString().c_str());
3679 #endif
3681 // if not shown in ActionProgression window, skip it
3682 if(!phrase->ShowInActionProgression)
3683 continue;
3685 // if not valid, skip it
3686 if(!phrase->isValid())
3687 continue;
3689 // **** for this phrase, build the required formula, with and without credits
3690 CReqSkillFormula skillFormulaProg; // For progression: No Credits
3691 CReqSkillFormula skillFormulaFull; // For info: full, With Credits
3692 // For all bricks
3693 uint i;
3694 for(i=0;i<phrase->Bricks.size();i++)
3696 CSBrickSheet *brick= pBM->getBrick(phrase->Bricks[i]);
3697 if(brick)
3699 // the brick formula is an OR of all skill required
3700 CReqSkillFormula brickFormula;
3702 // get all its required Skill
3703 for(uint j=0;j<brick->RequiredSkills.size();j++)
3705 CSkillValue sv;
3706 sv.Skill= brick->RequiredSkills[j].Skill;
3707 sv.Value= brick->RequiredSkills[j].Value;
3708 brickFormula.orV(sv);
3711 // if not empty
3712 if(!brickFormula.empty())
3714 // AND with phrase requirement formula
3715 skillFormulaFull.andV(brickFormula);
3716 // if the brick is not a credit
3717 if(!brick->isCredit())
3718 skillFormulaProg.andV(brickFormula);
3723 /* if the skillFormulaProg is empty(), and if the full formula is not, it is because
3724 this phrase is a Credit upgrade!!! we DO WANT see it in the skill tree
3726 Additionnaly, for special case of "after critical hit 3" for instance,
3727 where the credit is more restrictive than the options, take the full formula instead.
3728 Detect this case simply if the full formula is made of only ONE SkillValue
3730 if(skillFormulaProg.empty() || skillFormulaFull.singleValue())
3731 skillFormulaProg= skillFormulaFull;
3733 #ifdef PHRASE_DEBUG_VERBOSE
3734 skillFormulaFull.log("FULL");
3735 skillFormulaProg.log("PROG");
3736 #endif
3739 // **** For phrase info, store the Full formula
3740 _PhraseReqSkillMap[phrase->Id.asInt()]= skillFormulaFull;
3743 // **** For phrase progression filter
3744 skillsToInsert.clear();
3745 // parse all the required skill, both AND and OR
3746 std::list<CReqSkillFormula::CSkillValueAnd>::const_iterator itSVA;
3747 for(itSVA= skillFormulaProg.OrSkills.begin();itSVA!=skillFormulaProg.OrSkills.end();itSVA++)
3749 std::list<CSkillValue>::const_iterator itSV;
3750 for(itSV= itSVA->AndSkills.begin();itSV!=itSVA->AndSkills.end();itSV++)
3752 const CSkillValue &sv= *itSV;
3753 if(sv.Skill!=SKILLS::unknown)
3755 // get the actual Skill associated to the skillValue (with no sheet data error,
3756 // it should be == sv.Skill).
3757 SKILLS::ESkills skill= sv.Skill;
3758 uint32 value= sv.Value;
3759 // eg (SME,10) is incorrect (SME range is [20,50]) => take instead (SM,10)
3760 while(value<pSM->getMinSkillValue(skill))
3762 SKILLS::ESkills sp= pSM->getParent(skill);
3763 if(sp==SKILLS::unknown)
3764 break;
3765 else
3766 skill= sp;
3769 /* in case of (SM,25) (eg arise with auras), it means that for example,
3770 (SMA,25), or (SMB,25) or (SMC,25) are required.
3771 Then we have to add this progression in all those skills!
3772 => insert in those skills only!
3774 insertProgressionSkillRecurs(skill, value, skillReqLevel, skillsToInsert);
3779 // then parse the skill to insert list
3780 for(i=0;i<skillsToInsert.size();i++)
3782 SKILLS::ESkills skill= skillsToInsert[i];
3784 // append this phrase to this progression line
3785 CPhraseProgressInfo ppi;
3786 ppi.SheetId= phrase->Id.asInt();
3787 ppi.Level= skillReqLevel[skill];
3788 _ProgressionPhrases[skill].Phrases.push_back(ppi);
3790 // clean the level, for next phrase
3791 skillReqLevel[skill]= -1;
3796 // **** Sort each phrase sheet by type/cost
3797 // For all skills
3798 std::vector<CPhraseSortEntry> phraseSortEntry;
3799 for(uint i=0;i<SKILLS::NUM_SKILLS;i++)
3801 CPhraseProgression &pprog= _ProgressionPhrases[i];
3802 uint j;
3804 // for all phrase of this skills, fill sort struct
3805 phraseSortEntry.resize(pprog.Phrases.size());
3806 for(j=0;j<phraseSortEntry.size();j++)
3808 CPhraseSortEntry &pse= phraseSortEntry[j];
3809 pse.ProgressInfo= pprog.Phrases[j];
3811 // get the phrase best display brick
3812 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(pse.ProgressInfo.SheetId)));
3813 NLMISC::CSheetId brickSheet;
3814 if(phraseSheet)
3815 brickSheet= pBM->getSabrinaCom().getPhraseBestDisplayBrick(phraseSheet->Bricks);
3817 // take first the castable phrase
3818 pse.Castable= isPhraseCastable(phraseSheet);
3820 // sort by section
3821 pse.Section= getPhraseSectionFromLevel(pse.ProgressInfo.Level);
3823 // get the family of this brick as second sort key
3824 CSBrickSheet *brick= pBM->getBrick(brickSheet);
3825 if(brick)
3826 pse.Type= brick->BrickFamily;
3827 else
3828 pse.Type= 0;
3830 // get the icon of this brick as third sort key
3831 if(brick)
3832 pse.Icon= (uint32)brick->IdIcon;
3833 else
3834 pse.Icon= 0;
3836 // get the phrase Text as 4th sort key
3837 pse.Text= STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedName(CSheetId(pse.ProgressInfo.SheetId));
3838 // avoid mutliple sapce problem
3839 strFindReplace(pse.Text, " ", "");
3840 // replace each number with 001 format. toLower
3841 for(uint k=0;k<pse.Text.size();k++)
3843 if((uint8)pse.Text[k] < (uint8)'\x80' && isalpha(pse.Text[k]))
3844 pse.Text[k]= tolower(pse.Text[k]); // FIXME: toLowerAscii
3845 else if((uint8)pse.Text[k] < (uint8)'\x80' && isdigit(pse.Text[k]))
3847 uint32 number= 0;
3848 uint32 start= k;
3849 // get the whole number
3850 for(;k<pse.Text.size() && (uint8)pse.Text[k] < (uint8)'\x80' && isdigit(pse.Text[k]);k++)
3852 number*=10;
3853 number+= pse.Text[k] - '0';
3855 // format, and replace in string
3856 string newNumber= toString("%3d", number);
3857 pse.Text.replace(start, k-start, newNumber);
3858 // and skip this number
3859 k= start + (uint)newNumber.size();
3864 // sort
3865 sort(phraseSortEntry.begin(), phraseSortEntry.end());
3867 // write result
3868 for(j=0;j<phraseSortEntry.size();j++)
3870 pprog.Phrases[j]= phraseSortEntry[j].ProgressInfo;
3874 // **** append a callback to update the progression when a brick is learned
3875 pBM->appendBrickLearnedCallback(&_ProgressionUpdate);
3876 pSM->appendSkillChangeCallback(&_ProgressionUpdate);
3879 // ***************************************************************************
3880 enum {MaxSkillValue= 250};
3881 void CSPhraseManager::insertProgressionSkillRecurs(SKILLS::ESkills skill, uint32 value, sint *skillReqLevel, std::vector<SKILLS::ESkills> &skillsToInsert)
3883 CSkillManager *pSM= CSkillManager::getInstance();
3884 const std::vector<SKILLS::ESkills> &children= pSM->getChildren(skill);
3886 // Yoyo: special case for Skill value >250 (disabled bricks)....
3887 // Don't insert them
3888 if(value>MaxSkillValue)
3889 return;
3891 // if the value is under the max value of this skill, it's ok
3892 // Or special case for the MAX 250 value of skills, ie if the skill has no more children
3893 if(value < pSM->getMaxSkillValue(skill) || children.empty())
3895 // if not used, insert this skill in the "skill used in this phrase progression"
3896 if(skillReqLevel[skill]==-1)
3898 skillReqLevel[skill]= value;
3899 skillsToInsert.push_back(skill);
3901 // else maximize the level required for this phrase
3902 else
3904 skillReqLevel[skill]= max(skillReqLevel[skill], (sint)value);
3907 // else recurs to children
3908 else
3910 for(uint i=0;i<children.size();i++)
3912 insertProgressionSkillRecurs(children[i], value, skillReqLevel, skillsToInsert);
3917 // ***************************************************************************
3918 uint32 CSPhraseManager::getPhraseSectionFromLevel(uint32 level)
3920 uint32 ret= level/5;
3921 ret= min(ret, (uint32)((MaxSkillValue-1)/5));
3922 return ret;
3925 // ***************************************************************************
3926 void CSPhraseManager::getPhraseLevelFromSection(uint32 section, uint32 &minLevel, uint32 &maxLevel)
3928 minLevel= section*5;
3929 maxLevel= (section+1)*5-1;
3930 if(maxLevel==MaxSkillValue-1)
3931 maxLevel= MaxSkillValue;
3934 // ***************************************************************************
3935 void CSPhraseManager::buildPhraseBrickRequirement(uint32 phraseSheetId, std::vector<CSheetId> &bricks)
3937 CSBrickManager *pBM= CSBrickManager::getInstance();
3938 bricks.clear();
3940 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(phraseSheetId)));
3942 if(phraseSheet)
3944 uint i;
3946 // first get all bricks contained in this phrase
3947 set<CSheetId> brickSet;
3948 for(i=0;i<phraseSheet->Bricks.size();i++)
3950 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[i]);
3951 if(brick)
3952 brickSet.insert(brick->Id);
3955 // For all bricks of the phrase.
3956 for(i=0;i<phraseSheet->Bricks.size();i++)
3958 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[i]);
3959 if(brick)
3961 // Special case: For "Use Item enchantment" phrase, don't describe requirement
3962 if(brick->isProcEnchantment())
3964 bricks.clear();
3965 return;
3968 // For all its required bricks
3969 for(uint j=0;j<brick->RequiredBricks.size();j++)
3971 /* if the required brick is not in the current phrase, then it is required
3972 E.g: if a phrase has brickA and brickB, and if brickB require brickA
3973 then learning the phrase finally don't require brickA! (else won't be possible to learn it
3974 if this is the only phrase that offers brickA for instance)
3976 if( brickSet.find(brick->RequiredBricks[j])==brickSet.end() )
3978 bricks.push_back(brick->RequiredBricks[j]);
3986 // ***************************************************************************
3987 bool CSPhraseManager::fameRequirementOk(uint32 phraseSheetId)
3989 CSBrickManager *pBM= CSBrickManager::getInstance();
3991 CSPhraseSheet *phraseSheet= dynamic_cast<CSPhraseSheet*>(SheetMngr.get(CSheetId(phraseSheetId)));
3993 if(phraseSheet)
3995 uint i;
3997 // first get all bricks contained in this phrase
3998 set<CSheetId> brickSet;
3999 for(i=0;i<phraseSheet->Bricks.size();i++)
4001 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[i]);
4002 if(brick)
4003 brickSet.insert(brick->Id);
4006 // For all bricks of the phrase.
4007 for(i=0;i<phraseSheet->Bricks.size();i++)
4009 CSBrickSheet *brick= pBM->getBrick(phraseSheet->Bricks[i]);
4010 if(brick)
4012 // get player fame for this faction and check with min value needed
4013 if( brick->FactionIndex != CStaticFames::INVALID_FACTION_INDEX )
4016 //TODO
4021 return true;
4024 return false;
4027 // ***************************************************************************
4028 bool CSPhraseManager::phraseRequirementOk(uint32 phraseSheetId)
4030 CSBrickManager *pBM= CSBrickManager::getInstance();
4033 // **** check skill requirement for this phrase
4034 TPhraseReqSkillMap::const_iterator it= _PhraseReqSkillMap.find(phraseSheetId);
4035 if(it!=_PhraseReqSkillMap.end())
4037 const CReqSkillFormula &formula= it->second;
4039 if(!formula.evaluate())
4040 return false;
4044 // **** check fame requirement
4045 if( !fameRequirementOk(phraseSheetId) )
4047 return false;
4051 // **** check brick requirement
4052 std::vector<CSheetId> bricks;
4053 buildPhraseBrickRequirement(phraseSheetId, bricks);
4055 // For all required bricks
4056 for(uint i=0;i<bricks.size();i++)
4058 // if the required brick is not learned, fail
4059 if( !pBM->isBrickKnown(bricks[i]) )
4060 return false;
4064 // ok all requirement met
4065 return true;
4069 // ***************************************************************************
4070 bool CSPhraseManager::allowListBrickInHelp(const CSPhraseCom &phrase) const
4072 CSBrickManager *pBM= CSBrickManager::getInstance();
4074 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
4075 // broken phrase
4076 if(!rootBrick)
4077 return false;
4079 // don't list bricks for Charac upgrades
4080 if(BRICK_FAMILIES::isCharacBuyFamily(rootBrick->BrickFamily))
4081 return false;
4083 // don't list bricks for "use item enchant" phrase
4084 if(rootBrick->isProcEnchantment())
4085 return false;
4087 // common case
4088 return true;
4091 // ***************************************************************************
4092 void CSPhraseManager::getCombatWeaponRestriction(string &text, const CSPhraseCom &phrase, bool& usableWithMelee, bool& usableWithRange)
4094 text.clear();
4096 if(phrase.empty())
4097 return;
4099 CSBrickManager *pBM= CSBrickManager::getInstance();
4100 CSBrickSheet *rootBrick= pBM->getBrick(phrase.Bricks[0]);
4101 if(rootBrick && rootBrick->isCombat())
4103 // **** Build the "phrase skill compatible" formula.
4104 CReqSkillFormula phraseFormula= buildCombatPhraseSkillFormula(phrase.Bricks);
4106 // **** build the dorted array of ored skills
4107 std::vector<SKILLS::ESkills> skills;
4108 std::list<CReqSkillFormula::CSkillValueAnd>::iterator it(phraseFormula.OrSkills.begin()),
4109 end(phraseFormula.OrSkills.end());
4110 for(;it!=end;it++)
4112 CReqSkillFormula::CSkillValueAnd skillAnd= *it;
4113 // if the and is not a single value, then no chance to match (eg: SFR&SFM won't match any compareSkill)
4114 if(skillAnd.AndSkills.size()==1)
4116 SKILLS::ESkills skill= skillAnd.AndSkills.begin()->Skill;
4117 if(skill!=SKILLS::unknown)
4118 skills.push_back(skill);
4121 // sort by skill index. this is the convention, to be sure that we don't search "uiawrSFM_SFR"
4122 // while "uiawrSFR_SFM" is defined in en.uxt....
4123 std::sort(skills.begin(), skills.end());
4125 // used to know what weapon's family can be used with this phrase
4126 usableWithMelee = false;
4127 usableWithRange = false;
4129 // **** build the CI18N name
4130 std::string idName= "uiawr";
4131 for(uint i=0;i<skills.size();i++)
4133 string skillName = SKILLS::toString(skills[i]);
4134 idName+= skillName;
4135 if(i<skills.size()-1)
4136 idName+= "_";
4138 if( skillName.find("SFM") != std::string::npos )
4139 usableWithMelee = true;
4140 if( skillName.find("SFR") != std::string::npos )
4141 usableWithRange = true;
4144 // no restriction at all means everything possible
4145 if( !usableWithMelee && !usableWithRange )
4147 usableWithMelee = true;
4148 usableWithRange = true;
4151 // **** If found with this combination version
4152 if(CI18N::hasTranslation(idName))
4153 text= CI18N::get(idName);
4154 // The combination don't exist. just list each skill
4155 else
4157 for(uint i=0;i<skills.size();i++)
4159 text+= CI18N::get("uiawr" + SKILLS::toString(skills[i]));
4160 if(i<skills.size()-1)
4161 text+= CI18N::get("uiPhraseWRSeparator");
4167 // ***************************************************************************
4168 void CSPhraseManager::getCombatWeaponRestriction(string &text, sint32 phraseSheetId, bool& usableWithMelee, bool& usableWithRange)
4170 CSPhraseCom phrase;
4171 buildPhraseFromSheet(phrase, phraseSheetId);
4172 getCombatWeaponRestriction(text, phrase, usableWithMelee, usableWithRange);
4176 // ***************************************************************************
4177 CReqSkillFormula CSPhraseManager::buildCombatPhraseSkillFormula(const std::vector<NLMISC::CSheetId> &phraseBricks) const
4179 CSBrickManager *pBM= CSBrickManager::getInstance();
4180 CReqSkillFormula phraseFormula;
4182 // default: work with any Skill Fight weapon (ie all... :) )
4183 phraseFormula.andV(CSkillValue(SKILLS::SF));
4185 // for all bricks not Unknown
4186 for(uint i=0;i<phraseBricks.size();i++)
4188 CSBrickSheet *brick= pBM->getBrick(phraseBricks[i]);
4189 if(brick && brick->getSkill()!=SKILLS::unknown)
4191 CReqSkillFormula brickFormula;
4192 for(uint j=0;j<brick->UsedSkills.size();j++)
4194 brickFormula.orV(CSkillValue(brick->UsedSkills[j]));
4197 // and with the phraseFormula
4198 phraseFormula.andV(brickFormula);
4202 return phraseFormula;
4205 // ***************************************************************************
4206 uint32 CSPhraseManager::getSpellLevel(const std::vector<NLMISC::CSheetId> &phraseBricks) const
4208 CSBrickManager *pBM= CSBrickManager::getInstance();
4209 sint32 maxCost= 0;
4210 for(uint i=0;i<phraseBricks.size();i++)
4212 CSBrickSheet *brick= pBM->getBrick(phraseBricks[i]);
4213 if(brick && !brick->isCredit())
4215 maxCost= max(maxCost, brick->SabrinaCost);
4219 return maxCost;
4222 // ***************************************************************************
4223 void CSPhraseManager::getResistMagic(bool resistMagic[RESISTANCE_TYPE::NB_RESISTANCE_TYPE], const std::vector<NLMISC::CSheetId> &phraseBricks)
4225 // reset
4226 for(uint i=0;i<RESISTANCE_TYPE::NB_RESISTANCE_TYPE;i++)
4228 resistMagic[i]= false;
4231 // set
4232 CSBrickManager *pBM= CSBrickManager::getInstance();
4233 for(uint i=0;i<phraseBricks.size();i++)
4235 CSBrickSheet *brick= pBM->getBrick(phraseBricks[i]);
4236 if(brick && brick->MagicResistType!=RESISTANCE_TYPE::None)
4238 resistMagic[brick->MagicResistType]= true;
4243 // ***************************************************************************
4244 uint32 CSPhraseManager::getMemorizedPhraseIfLastOrNewSlot(uint32 memoryLine, uint32 memoryIndex)
4246 // \toto yoyo: There is a Network BUG which prevents us from simply doing {Forget(),Delete()} Old,
4247 // then {Learn(),Memorize()} new (Messages are shuffled).
4249 // If the memory slot has a Phrase
4250 uint32 oldPhraseId= getMemorizedPhrase(memoryLine, memoryIndex);
4251 if(oldPhraseId)
4253 // if this is the last referenced, then return this phrase id
4254 if(countAllThatUsePhrase(oldPhraseId)==1)
4256 return oldPhraseId;
4260 // else get New Free Slot
4261 return allocatePhraseSlot();
4264 // ***************************************************************************
4265 void CSPhraseManager::fullDeletePhraseIfLast(uint32 memoryLine, uint32 memorySlot)
4267 uint32 phraseId= getMemorizedPhrase(memoryLine, memorySlot);
4268 // not a phrase? no-op
4269 if(!phraseId)
4270 return;
4272 // more than 1 use this slot? => no problem (will be forgeted/replaced)
4273 if(countAllThatUsePhrase(phraseId)>1)
4274 return;
4276 // else delete this phrase (server etc....)
4277 fullDelete(phraseId);
4281 // ***************************************************************************
4282 string CSPhraseManager::getForageExtractionPhraseEcotypeFmt(const CSPhraseCom &phrase)
4284 CSBrickManager *pBM= CSBrickManager::getInstance();
4286 static string tspecString= "FG_ECT_SPC: ";
4288 // Test all brick, if find a ecotype specialisation, return fmt
4289 for(uint i=0;i<phrase.Bricks.size();i++)
4291 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
4292 // if a forage extraction brick
4293 if(brick)
4295 // Parse all properties of the brick, if FG_ECT_SPC, this is a terrain speciliasing
4296 for(uint j=0;j<brick->Properties.size();j++)
4298 const string &text= brick->Properties[j].Text;
4299 if(text.compare(0, tspecString.size(), tspecString)==0)
4301 // extract the associated terrain
4302 string terran= text.substr(tspecString.size(), string::npos);
4303 terran= string("uihelpPhraseForageSuccess")+terran;
4304 // try to get the associated I18N
4305 if(CI18N::hasTranslation(terran))
4306 return CI18N::get(terran);
4312 // Failure, display all
4313 return CI18N::get("uihelpPhraseForageSuccessAll");
4316 // ***************************************************************************
4317 bool CSPhraseManager::findDefaultAttack(uint32 &memoryLine, uint32 &memorySlot)
4319 CSBrickManager *pBM= CSBrickManager::getInstance();
4321 if(_Memories.empty())
4322 return false;
4324 // **** Search in all memory lines, but try the currently selected one
4325 std::deque<uint> memoryLineSort;
4326 memoryLineSort.resize(_Memories.size());
4327 uint32 i;
4328 for(i=0;i<memoryLineSort.size();i++)
4329 memoryLineSort[i]= i;
4330 // remove and push_front the selected memory line
4331 if(_SelectedMemoryDB>=0 && _SelectedMemoryDB<(sint)memoryLineSort.size())
4333 memoryLineSort.erase(memoryLineSort.begin()+_SelectedMemoryDB);
4334 memoryLineSort.push_front(_SelectedMemoryDB);
4337 // **** Parse all memories
4338 for(i=0;i<memoryLineSort.size();i++)
4340 CMemoryLine &memLine= _Memories[memoryLineSort[i]];
4341 for(uint j=0;j<PHRASE_MAX_MEMORY_SLOT;j++)
4343 if(memLine.Slot[j].isPhrase())
4345 const CSPhraseCom &phrase= getPhrase(memLine.Slot[j].Id);
4346 // The phrase must have only one brick: the default attack brick
4347 if( phrase.Bricks.size() == 1 )
4349 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[0]);
4350 // combat root brick?
4351 if( brick && brick->isRoot() && brick->isCombat() )
4353 // default attack!
4354 memoryLine= memoryLineSort[i];
4355 memorySlot= j;
4356 return true;
4363 return false;
4366 // ***************************************************************************
4367 bool CSPhraseManager::avoidCyclicForPhrase(const CSPhraseCom &phrase) const
4369 CSBrickManager *pBM= CSBrickManager::getInstance();
4371 for(uint i=0;i<phrase.Bricks.size();i++)
4373 CSBrickSheet *brick= pBM->getBrick(phrase.Bricks[i]);
4374 if(brick && brick->AvoidCyclic)
4375 return true;
4378 return false;
4381 // ***************************************************************************
4382 bool CSPhraseManager::avoidCyclicForPhrase(sint32 phraseSheetId) const
4384 return avoidCyclicForPhrase(getPhrase(phraseSheetId));
4388 // ***************************************************************************
4389 uint CSPhraseManager::fillRoleMasterGenericPhrase(uint32 rmFlags, uint32 rmRace)
4391 CSBrickManager *pBM= CSBrickManager::getInstance();
4393 uint destIndex= 0;
4394 uint numFill= 0;
4396 // not initialized => abort
4397 if(_BotChatPhraseSheetLeaves.empty())
4398 return 0;
4400 // *** For all phrase sheets
4401 CSheetManager::TEntitySheetMap::const_iterator it;
4402 for(it=SheetMngr.getSheets().begin();it!=SheetMngr.getSheets().end();it++)
4404 if(it->second.EntitySheet->Type==CEntitySheet::SPHRASE)
4406 // *** Filter RoleMaster and basics
4407 const CSPhraseSheet *phrase= safe_cast<const CSPhraseSheet*>(it->second.EntitySheet);
4408 // Not valid phrase are not sellable (some brick no more exist)
4409 if(!phrase->isValid())
4410 continue;
4411 // ShowInAPOnlyIfLearnt are typically special mission phrase, skip them
4412 if(phrase->ShowInAPOnlyIfLearnt)
4413 continue;
4414 // test rmFlags against phrase name
4415 if( !ROLEMASTER_FLAGS::canSellPhrase(rmFlags, phrase->Id.toString()) )
4416 continue;
4417 // test race (test all bricks)
4418 bool raceOk= true;
4419 uint i;
4420 for(i=0;i<phrase->Bricks.size();i++)
4422 CSBrickSheet *brick= pBM->getBrick(phrase->Bricks[i]);
4423 // if the brick exist, not a Common (no restriction), and not equal to rolemaster race,
4424 // then can't sell this phrase
4425 if( brick && brick->CivRestriction!=EGSPD::CPeople::Common &&
4426 brick->CivRestriction!=(EGSPD::CPeople::TPeople)rmRace )
4428 raceOk= false;
4429 break;
4432 // if some brick failed, skip
4433 if(!raceOk)
4434 continue;
4437 // *** Filter not fully known phrases
4438 bool fullKnown= true;
4439 for(i=0;i<phrase->Bricks.size();i++)
4441 CSBrickSheet *brick= pBM->getBrick(phrase->Bricks[i]);
4442 // if the brick exist, and not known then OK, all are not known
4443 if(brick && !pBM->isBrickKnown(phrase->Bricks[i]))
4445 fullKnown= false;
4446 break;
4449 // if all bricks of this phrase are known, then don't need to sell!
4450 if(fullKnown)
4451 continue;
4454 // *** Filter phrases that are ok with requirement
4455 if(!phraseRequirementOk(phrase->Id.asInt()))
4456 continue;
4459 // *** All filter pass, add this phrase
4460 if(_BotChatPhraseSheetLeaves[destIndex])
4461 _BotChatPhraseSheetLeaves[destIndex]->setValue32(phrase->Id.asInt());
4463 // next
4464 destIndex++;
4465 // full DB filled?
4466 if(destIndex==_BotChatPhraseSheetLeaves.size())
4467 break;
4471 // nb really filled
4472 numFill= destIndex;
4474 // *** Fill others with 0!
4475 for(;destIndex<_BotChatPhraseSheetLeaves.size();destIndex++)
4477 if(_BotChatPhraseSheetLeaves[destIndex])
4478 _BotChatPhraseSheetLeaves[destIndex]->setValue32(0);
4481 return numFill;
4485 // ***************************************************************************
4486 void CSPhraseManager::getPhraseSectionBoundFromSkillFilter(sint &minSectionId, sint &maxSectionId)
4488 CSkillManager *pSM= CSkillManager::getInstance();
4489 SKILLS::ESkills filter= getBookSkillFilter();
4490 // get its skill bound (eg: 20-50)
4491 sint minSkillValue= pSM->getMinSkillValue(filter);
4492 sint maxSkillValue= pSM->getMaxSkillValue(filter);
4493 minSkillValue= max(minSkillValue, 0);
4494 maxSkillValue= max(maxSkillValue, 0);
4495 // get its related section id bound (eg: 3-10)
4496 minSectionId= minSkillValue/5 -1;
4497 // NB: 50/5==10 is already exclusive (I mean all actions of DefensiveMagic Skill are 49 max)
4498 maxSectionId= maxSkillValue/5;
4501 // ***************************************************************************
4502 sint32 CSPhraseManager::getSheetFromPhrase(const CSPhraseCom &phrase) const
4504 TPhraseToSheet::const_iterator it = _PhraseToSheet.find(phrase);
4505 if (it == _PhraseToSheet.end()) return 0;
4506 return it->second;
4509 // ***************************************************************************
4510 uint32 CSPhraseManager::getTotalActionMalus(const CSPhraseCom &phrase) const
4512 CInterfaceManager *pIM = CInterfaceManager::getInstance();
4513 CSBrickManager *pBM = CSBrickManager::getInstance();
4514 uint32 totalActionMalus = 0;
4515 CCDBNodeLeaf *actMalus = _TotalMalusEquipLeaf ? &*_TotalMalusEquipLeaf
4516 : &*(_TotalMalusEquipLeaf = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TOTAL_MALUS_EQUIP", false));
4517 // root brick must not be Power or aura, because Action malus don't apply to them
4518 // (ie leave 0 ActionMalus for Aura or Powers
4519 if (!phrase.Bricks.empty())
4521 CSBrickSheet *rootBrick = pBM->getBrick(phrase.Bricks[0]);
4522 if (!rootBrick)
4523 nlerror("Invalid root sbrick in sphrase_com '%s'", phrase.Name.toUtf8().c_str());
4524 else if (actMalus && !rootBrick->isSpecialPower())
4525 totalActionMalus = actMalus->getValue32();
4527 return totalActionMalus;
4531 // ***************************************************************************
4532 CCDBNodeLeaf *CSPhraseManager::getRegenTickRangeDbLeaf(uint powerIndex) const
4534 CInterfaceManager *pIM = CInterfaceManager::getInstance();
4535 CCDBNodeLeaf *dbLeaf = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FLAGS:BRICK_TICK_RANGE:%d:TICK_RANGE", (int) powerIndex), false);
4536 return dbLeaf;
4539 // ***************************************************************************
4540 CTickRange CSPhraseManager::getRegenTickRange(uint powerIndex) const
4542 CTickRange tickRange;
4543 CCDBNodeLeaf *dbLeaf = getRegenTickRangeDbLeaf(powerIndex);
4544 if (dbLeaf)
4546 tickRange.StartTick = (NLMISC::TGameCycle) dbLeaf->getValue32();
4547 tickRange.EndTick = (NLMISC::TGameCycle) (((uint64) dbLeaf->getValue64()) >> 32);
4549 return tickRange;
4552 // ***************************************************************************
4553 void CSPhraseManager::setRegenTickRange(uint powerIndex, const CTickRange &tickRange)
4555 CCDBNodeLeaf *dbLeaf = getRegenTickRangeDbLeaf(powerIndex);
4556 if (dbLeaf)
4558 uint64 value = uint64(tickRange.StartTick) | (uint64(tickRange.EndTick) << 32);
4559 dbLeaf->setValue64(value);
4564 // ***************************************************************************
4565 int CSPhraseComAdpater::luaGetCastTime(CLuaState &ls)
4567 if (Phrase.Bricks.empty())
4569 ls.push(0.0);
4570 return 1;
4572 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4573 float castTime;
4574 float castTimeMalus;
4575 pPM->getPhraseCastTime(Phrase, pPM->getTotalActionMalus(Phrase), castTime, castTimeMalus);
4576 ls.push(castTime + castTimeMalus);
4577 return 1;
4580 // ***************************************************************************
4581 int CSPhraseComAdpater::luaGetCastRange(CLuaState &ls)
4583 if (Phrase.Bricks.empty())
4585 ls.push((sint)0);
4586 return 1;
4588 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4589 sint range;
4590 sint rangeMalus;
4591 pPM->getPhraseMagicRange(this->Phrase, pPM->getTotalActionMalus(Phrase), range, rangeMalus);
4592 ls.push(range + rangeMalus);
4593 return 1;
4596 // ***************************************************************************
4597 int CSPhraseComAdpater::luaGetHpCost(CLuaState &ls)
4599 if (Phrase.Bricks.empty())
4601 ls.push((sint)0);
4602 return 1;
4604 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4605 sint hpCost;
4606 sint hpCostMalus;
4607 pPM->getPhraseHpCost(this->Phrase, pPM->getTotalActionMalus(Phrase), hpCost, hpCostMalus);
4608 ls.push(hpCost + hpCostMalus);
4609 return 1;
4612 // ***************************************************************************
4613 int CSPhraseComAdpater::luaGetSapCost(CLuaState &ls)
4615 if (Phrase.Bricks.empty())
4617 ls.push((sint)0);
4618 return 1;
4620 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4621 sint sapCost;
4622 sint sapCostMalus;
4623 pPM->getPhraseSapCost(this->Phrase, pPM->getTotalActionMalus(Phrase), sapCost, sapCostMalus);
4624 ls.push(sapCost + sapCostMalus);
4625 return 1;
4628 // ***************************************************************************
4629 int CSPhraseComAdpater::luaGetSuccessRate(CLuaState &ls)
4631 if (Phrase.Bricks.empty())
4633 ls.push((sint)0);
4634 return 1;
4636 CSPhraseManager *pPM = CSPhraseManager::getInstance(); ;
4637 ls.push(pPM->getPhraseSuccessRate(this->Phrase));
4638 return 1;
4642 // ***************************************************************************
4643 int CSPhraseComAdpater::luaGetFocusCost(CLuaState &ls)
4645 if (Phrase.Bricks.empty())
4647 ls.push((sint)0);
4648 return 1;
4650 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4651 sint focusCost;
4652 sint focusCostMalus;
4653 pPM->getPhraseFocusCost(this->Phrase, pPM->getTotalActionMalus(Phrase), focusCost, focusCostMalus);
4654 ls.push(focusCost + focusCostMalus);
4655 return 1;
4658 // ***************************************************************************
4659 int CSPhraseComAdpater::luaGetStaCost(CLuaState &ls)
4661 if (Phrase.Bricks.empty())
4663 ls.push((sint)0);
4664 return 1;
4666 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4667 sint staCost;
4668 sint staCostMalus;
4669 pPM->getPhraseStaCost(this->Phrase, pPM->getTotalActionMalus(Phrase), staCost, staCostMalus);
4670 ls.push(staCost + staCostMalus);
4671 return 1;
4675 // ***************************************************************************
4676 int CSPhraseComAdpater::luaGetName(CLuaState &ls)
4678 #ifdef RYZOM_LUA_UCSTRING
4679 CLuaIHM::push(ls, this->Phrase.Name);
4680 #else
4681 ls.push(this->Phrase.Name.toUtf8());
4682 #endif
4683 return 1;
4687 // ***************************************************************************
4688 int CSPhraseComAdpater::luaGetDesc(CLuaState &ls)
4690 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4691 sint32 phraseSheetID = pPM->getSheetFromPhrase(Phrase);
4692 if (phraseSheetID != 0)
4694 // is it a built-in phrase?
4695 const char *desc = STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedDescription(NLMISC::CSheetId(phraseSheetID));
4696 if (*desc)
4698 #ifdef RYZOM_LUA_UCSTRING
4699 ucstring uc = ucstring::makeFromUtf8(desc); // Compatibility
4700 CLuaIHM::push(ls, uc);
4701 #else
4702 ls.push(desc);
4703 #endif
4704 return 1;
4707 return 0;
4710 // ***************************************************************************
4711 int CSPhraseComAdpater::luaIsCombatPhrase(CLuaState &ls)
4713 if(Phrase.empty())
4715 ls.push(false);
4716 return 1;
4718 CSBrickManager *pBM= CSBrickManager::getInstance();
4719 CSBrickSheet *rootBrick= pBM->getBrick(Phrase.Bricks[0]);
4720 if(!rootBrick)
4722 ls.push(false);
4723 return 1;
4725 ls.push(rootBrick->isCombat());
4726 return 1;
4729 // ***************************************************************************
4730 int CSPhraseComAdpater::luaIsMagicPhrase(CLuaState &ls)
4732 if(Phrase.empty())
4734 ls.push(false);
4735 return 1;
4737 CSBrickManager *pBM= CSBrickManager::getInstance();
4738 CSBrickSheet *rootBrick= pBM->getBrick(Phrase.Bricks[0]);
4739 if(!rootBrick)
4741 ls.push(false);
4742 return 1;
4744 ls.push(rootBrick->isMagic());
4745 return 1;
4748 // ***************************************************************************
4749 int CSPhraseComAdpater::luaIsPowerPhrase(CLuaState &ls)
4751 if(Phrase.empty())
4753 ls.push(false);
4754 return 1;
4756 CSBrickManager *pBM= CSBrickManager::getInstance();
4757 CSBrickSheet *rootBrick= pBM->getBrick(Phrase.Bricks[0]);
4758 if(!rootBrick)
4760 ls.push(false);
4761 return 1;
4763 ls.push(rootBrick->isSpecialPower());
4764 return 1;
4767 // ***************************************************************************
4768 int CSPhraseComAdpater::luaGetRegenTime(CLuaState &ls)
4770 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4771 ls.push((sint)pPM->getRegenTime(Phrase));
4772 return 1;
4775 // ***************************************************************************
4776 int CSPhraseComAdpater::luaGetTotalRegenTime(CLuaState &ls)
4778 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4779 ls.push((sint)pPM->getTotalRegenTime(Phrase));
4780 return 1;
4783 // ***************************************************************************
4784 int CSPhraseComAdpater::luaGetPowerDisableTime(CLuaState &ls)
4786 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4787 ls.push((sint)pPM->getPowerDisableTime(Phrase));
4788 return 1;
4791 //////////////
4792 // COMMANDS //
4793 //////////////
4795 #ifdef NL_DEBUG
4797 NLMISC_COMMAND(regenTickRange, "Emulate regen tick range locally for a memory slot)", "")
4799 if (args.size() != 1 && args.size() != 2) return false;
4800 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4801 uint32 phraseId;
4802 NLMISC::fromString(args[0], phraseId);
4803 const CSPhraseCom &phrase = pPM->getPhrase(phraseId);
4804 uint64 powerBitField = CSPhraseManager::getPhraseRequiredFlags(phrase);
4805 TGameCycle duration;
4806 if (args.size() >= 2)
4808 NLMISC::fromString(args[1], duration);
4810 else
4812 duration = pPM->getPowerDisableTime(phrase);
4814 for (uint b = 0; b < 64; ++b)
4816 if (powerBitField & (uint64(1) << b))
4818 pPM->setRegenTickRange(b, CTickRange(LastGameCycle, LastGameCycle + duration));
4821 pPM->touchRegenTickRangeFlag();
4822 return true;
4825 NLMISC_COMMAND(forceUpdatePhrases, "Force to update the display of phrases", "")
4827 if (!args.empty()) return false;
4828 CSPhraseManager *pPM = CSPhraseManager::getInstance();
4829 pPM->touchRegenTickRangeFlag();
4830 pPM->updateAllMemoryCtrlState();
4831 pPM->updateAllActionRegen();
4832 return true;
4835 #endif