1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
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>
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/>.
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"
47 using namespace NLMISC
;
48 using namespace NLGEORGES
;
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()
89 // ***************************************************************************
90 CSPhraseManager::CSPhraseManager()
93 for(uint i
=0;i
<NumSuccessTable
;i
++)
94 _SuccessTableSheet
[i
]= NULL
;
95 _RegenTickRangeTouched
= true;
98 // ***************************************************************************
99 CSPhraseManager::~CSPhraseManager()
104 // ***************************************************************************
105 void CSPhraseManager::initInGame()
110 static bool registerClassDone
= false;
113 _InitInGameDone
= true;
115 // Init Database values.
116 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
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");
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");
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
++)
151 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION
[j
] + ":" + toString(i
) + ":SHEET");
153 _ProgressionDbSheets
[j
][i
]= node
;
155 node
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION
[j
] + ":" + toString(i
) + ":LEVEL");
157 _ProgressionDbLevels
[j
][i
]= node
;
159 node
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_PROGRESSION
[j
] + ":" + toString(i
) + ":LOCKED");
161 _ProgressionDbLocks
[j
][i
]= node
;
165 // init the UI Next Execute slot
167 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_EXECUTE_NEXT
);
169 _NextExecuteLeaf
= node
;
170 node
= NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_EXECUTE_NEXT_IS_CYCLIC
);
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
189 // Load the success table here
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();
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()
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)
231 // enlargePhraseClient
232 if(slot
>=_PhraseClient
.size())
233 _PhraseClient
.resize(slot
+1);
236 _PhraseMap
[slot
]= phrase
;
237 // increment the phrase version.
238 _PhraseClient
[slot
].Version
++;
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
)
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())
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
);
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
;
313 // ***************************************************************************
314 sint32
CSPhraseManager::getPhraseVersion(uint32 slot
) const
316 if(slot
>=_PhraseClient
.size())
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())
329 _PhraseClient
[slot
].Lock
= false;
330 // and so update the book.
336 // ***************************************************************************
337 void CSPhraseManager::receiveBotChatConfirmBuy(uint16 phraseId
, bool confirm
)
339 // if confirm => unlock
343 unlockPhrase(phraseId
);
345 // Then try to auto memorize it.
346 const CSPhraseCom
&phrase
= getPhrase(phraseId
);
349 CSBrickManager
*pBM
= CSBrickManager::getInstance();
350 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
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
++)
361 if( !isMemorizedMacro(dstMemoryLine
, i
) &&
362 getMemorizedPhrase(dstMemoryLine
, i
)==0 )
369 // If free slot found in this memory line
373 memorizePhrase(dstMemoryLine
, dstMemorySlot
, phraseId
);
376 sendMemorizeToServer(dstMemoryLine
, dstMemorySlot
, phraseId
);
383 // Ugly: must update ALL the memoryBar in case of dstMemoryLine is the selected one
384 updateAllMemoryCtrlState();
389 contextHelp ("action_book");
393 erasePhrase(phraseId
);
397 // ***************************************************************************
398 void CSPhraseManager::forgetPhrase(uint32 memoryLine
, uint32 memorySlot
)
400 if(memorySlot
>=PHRASE_MAX_MEMORY_SLOT
)
402 // if the slot eihter don't exist, abort
403 if(memoryLine
>=_Memories
.size())
405 // if the slot is not a phrase
406 if(!_Memories
[memoryLine
].Slot
[memorySlot
].isPhrase())
410 _Memories
[memoryLine
].Slot
[memorySlot
].IsMacro
= false;
411 _Memories
[memoryLine
].Slot
[memorySlot
].Id
= 0;
414 if((sint32
)memoryLine
==_SelectedMemoryDB
)
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())
432 if(memorySlot
>=PHRASE_MAX_MEMORY_SLOT
)
434 if(!_Memories
[memoryLine
].Slot
[memorySlot
].isPhrase())
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
)
445 if(slot
>=_PhraseClient
.size())
447 // Can memorize only a phrase of the book
448 if(slot
<BookStartSlot
)
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())
459 // enlarge memory if needed
460 if(memoryLine
>=_Memories
.size())
461 _Memories
.resize(memoryLine
+1);
464 _Memories
[memoryLine
].Slot
[memorySlot
].IsMacro
= false;
465 _Memories
[memoryLine
].Slot
[memorySlot
].Id
= slot
;
468 if((sint32
)memoryLine
==_SelectedMemoryDB
)
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
)
487 if(_SelectedMemoryDB
!=memoryLine
)
489 _SelectedMemoryDB
= memoryLine
;
490 // since memory selection changes then must update all the DB and the Ctrl states
492 updateAllMemoryCtrlState();
493 updateAllMemoryCtrlRegenTickRange();
494 // must update also the execution views
495 updateExecutionDisplay();
499 void CSPhraseManager::selectMemoryLineDBalt(sint32 memoryLine
)
504 if(_SelectedMemoryDBalt
!=memoryLine
)
506 _SelectedMemoryDBalt
= memoryLine
;
507 // since memory selection changes then must update all the DB and the Ctrl states
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
523 if(_SelectedMemoryDB
==-1 || _SelectedMemoryDB
>=(sint32
)_Memories
.size())
525 for(uint i
=0;i
<PHRASE_MAX_MEMORY_SLOT
;i
++)
527 _MemoryDbLeaves
[i
]->setValue32(0);
532 for(uint i
=0;i
<PHRASE_MAX_MEMORY_SLOT
;i
++)
534 CMemorySlot
&slot
= _Memories
[_SelectedMemoryDB
].Slot
[i
];
536 _MemoryDbLeaves
[i
]->setValue32(0);
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);
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);
558 _MemoryAltDbLeaves
[i
]->setValue32(slotAlt
.Id
);
563 // ***************************************************************************
564 void CSPhraseManager::updateMemoryDBSlot(uint32 memorySlot
)
566 // If DB not inited, no-op
570 if(_SelectedMemoryDB
==-1 || _SelectedMemoryDB
>=(sint32
)_Memories
.size())
573 if(memorySlot
<PHRASE_MAX_MEMORY_SLOT
)
575 CMemorySlot
&slot
= _Memories
[_SelectedMemoryDB
].Slot
[memorySlot
];
577 _MemoryDbLeaves
[memorySlot
]->setValue32(0);
579 _MemoryDbLeaves
[memorySlot
]->setValue32(slot
.Id
);
581 CMemorySlot
&slotAlt
= _Memories
[_SelectedMemoryDBalt
].Slot
[memorySlot
];
583 if(!slotAlt
.isPhrase())
584 _MemoryAltDbLeaves
[memorySlot
]->setValue32(0);
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
603 if(phraseSheet
->Castable
)
605 // check Charac Buying (compatibility)
606 if(!isPhraseCharacBuying(phraseSheet
))
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())
628 CSBrickSheet
*brickSheet
= pBM
->getBrick(phraseSheet
->Bricks
[0]);
629 // if not a charac buying phrase
630 if(brickSheet
&& BRICK_FAMILIES::isCharacBuyFamily(brickSheet
->BrickFamily
) )
636 // ***************************************************************************
637 uint32
CSPhraseManager::allocatePhraseSlot()
639 if(_FreeSlots
.empty())
642 if(_MaxSlotSet
>=PHRASE_MAX_ID
)
646 return _MaxSlotSet
+1;
650 uint32 val
= _FreeSlots
.back();
651 _FreeSlots
.pop_back();
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
)
669 // ***************************************************************************
670 void CSPhraseManager::setBookFilter(BRICK_TYPE::EBrickType brickTypeFilter
, SKILLS::ESkills skillFilter
)
673 _BookBrickTypeFilter
= brickTypeFilter
;
674 _BookSkillFitler
= skillFilter
;
676 // the DB book may change
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
)
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)
695 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
699 // If decide to filter by brickType
700 if(_BookBrickTypeFilter
!=BRICK_TYPE::UNKNOWN
)
702 if( BRICK_FAMILIES::brickType(rootBrick
->BrickFamily
) == _BookBrickTypeFilter
)
705 // else filter by Skill
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
))
722 // suppose same for magic, craft, and forage
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
]);
731 // if the filter is an ancestor, ok! (NB: if brick skill unknown then it will fail!)
732 if(pSM
->isSkillAncestor(_BookSkillFitler
, brick
->getSkill()))
743 // ***************************************************************************
744 void CSPhraseManager::updateBookDB()
746 // If DB not inited, no-op
750 // Fill All the book.
751 TPhraseMap::const_iterator it
= _PhraseMap
.begin();
752 sint numBookFill
= (sint
)_PhraseMap
.size();
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
) )
767 // fill with the phrase id
768 _BookDbLeaves
[i
]->setValue32(it
->first
);
773 // if no more place on book, stop
774 if(i
>=PHRASE_MAX_BOOK_SLOT
)
776 numBookFill
= PHRASE_MAX_BOOK_SLOT
;
784 // reset old no more used to empty
785 for(i
=numBookFill
;i
<(sint
)_LastBookNumDbFill
;i
++)
787 _BookDbLeaves
[i
]->setValue32(0);
791 _LastBookNumDbFill
= numBookFill
;
794 // ***************************************************************************
795 void CSPhraseManager::buildPhraseFromSheet(CSPhraseCom
&phrase
, sint32 sheetId
)
797 CSPhraseSheet
*phraseSheet
= dynamic_cast<CSPhraseSheet
*>(SheetMngr
.get(CSheetId(sheetId
)));
800 // get localized Name
801 phrase
.Name
= STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedName(CSheetId(sheetId
));
803 phrase
.Bricks
.clear();
804 for(uint i
=0;i
<phraseSheet
->Bricks
.size();i
++)
805 phrase
.Bricks
.push_back(phraseSheet
->Bricks
[i
]);
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
)
835 extern void debugUpdateActionBar();
836 debugUpdateActionBar();
838 // Send the memorize to server
842 const string sMsg
= "PHRASE:MEMORIZE";
843 if(GenericMsgHeaderMngr
.pushNameToStream(sMsg
, out
))
845 CSPhraseCom phrase
= getPhrase(phraseId
);
846 // free Band; don't send name
849 uint8 memoryId
= (uint8
)memoryLine
;
850 uint8 slotId
= (uint8
)memorySlot
;
851 uint16 pid
= (uint16
)phraseId
;
852 out
.serial(memoryId
);
857 //nlinfo("impulseCallBack : %s %d %d %d (phrase) sent", sMsg.c_str(), memoryId, slotId, pid);
860 nlwarning("impulseCallBack : unknown message name : '%s'.", sMsg
.c_str());
864 // ***************************************************************************
865 void CSPhraseManager::sendForgetToServer(uint32 memoryLine
, uint32 memoryIndex
)
871 // Send the forget to server.
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
);
884 //nlinfo("impulseCallBack : %s %d %d sent", sMsg.c_str(), memoryId, slotId);
887 nlwarning("impulseCallBack : unknown message name : '%s'.", sMsg
.c_str());
891 // ***************************************************************************
892 void CSPhraseManager::reset()
895 _PhraseClient
.clear();
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;
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;
962 // ***************************************************************************
963 string
CSPhraseManager::formatMalus(sint base
, sint malus
)
966 return toString("@{F80F}%d@{FFFF} (%d)", base
+malus
, base
);
968 return toString(base
);
971 // ***************************************************************************
972 string
CSPhraseManager::formatMalus(float base
, float malus
)
975 return toString("@{F80F}%.1f@{FFFF} (%.1f)", base
+malus
, base
);
977 return toString("%.1f", base
);
980 // ***************************************************************************
981 string
CSPhraseManager::formatBonusMalus(sint32 base
, sint32 mod
)
986 str
= toString("@{FFFF}") + toString(base
);
989 if( mod
> 0 ) // bonus
991 str
= "@{0F0F}" + toString( base
+ mod
)
1000 str
= "@{E42F}" + toString( base
+ mod
)
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();
1019 bool castable
= phraseSheetId
==0 || isPhraseCastable(phraseSheetId
);
1022 // For true phraseId, or castable .sphrase only
1025 // **** Get the format from EnergyType
1026 CSBrickSheet
*rootBrick
= NULL
;
1027 if(phrase
.Bricks
.size())
1028 rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
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");
1051 text
= CI18N::get("uihelpPhraseOtherFormat");
1053 // if composition, cut the text before the tag (including)
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
1063 strFindReplace(text
, compoTag
, string() );
1067 text
= CI18N::get(specialPhraseFormat
);
1072 // **** Phrase info basics
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())
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
);
1103 for(uint i
=0;i
<RESISTANCE_TYPE::NB_RESISTANCE_TYPE
;i
++)
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
);
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
);
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();
1166 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
1167 if( inv
->isRangeWeaponItem(rightHandSheet
) )
1169 nodeSM
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:RANGE", false);
1173 nodeSM
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MELEE", false);
1178 // phrase usable only in melee fight
1179 if( usableWithMelee
)
1181 nodeSM
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MELEE", false);
1184 // phrase usable only in range fight
1185 if( usableWithRange
)
1187 nodeSM
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:RANGE", false);
1191 if(rootBrick
->isMagic())
1193 nodeSM
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:CHARACTER_INFO:SUCCESS_MODIFIER:MAGIC", false);
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"
1208 strFindReplace(text
, "%range", CI18N::get("uihelpPhraseRangeSelf"));
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);
1228 commonModifier
= nodeSM
->getValue32();
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
));
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
);
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
);
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
);
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
1287 // special for charac buying
1288 if(isPhraseCharacBuying(phraseSheetId
))
1289 text
= CI18N::get("uihelpPhraseCharacteristic");
1291 text
= CI18N::get("uihelpPhraseNotCastableFormat");
1294 // **** Special .sphrase description
1298 string desc
= STRING_MANAGER::CStringManagerClient::getSPhraseLocalizedDescription(CSheetId(phraseSheetId
));
1300 strFindReplace(text
, "%desc", string());
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')
1308 // replace in format
1309 strFindReplace(text
, "%desc", desc
);
1314 strFindReplace(text
, "%desc", string());
1317 // **** Special .sphrase requirement
1318 if(phraseSheetId
&& wantRequirement
)
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
1335 formula
.getInfoText(textForm
);
1340 strFindReplace(text
, "%req", reqText
);
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())
1355 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
1359 // get the Skill value (according to phrase type etc...)
1360 TSuccessTable sTable
;
1362 if(rootBrick
->isCombat())
1364 // retrieve the current Skill of the Item in RightHand
1365 SKILLS::ESkills skill
= getRightHandItemSkill();
1368 skillValue
= pSM
->getSkillValueMaxBranch(skill
);
1370 // use the combat table
1373 else if(rootBrick
->isMagic())
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
]);
1384 SKILLS::ESkills skill
= brick
->getSkill();
1385 if(skill
!=SKILLS::unknown
)
1387 skillValue
+= pSM
->getSkillValueMaxBranch(skill
);
1395 skillValue
/= numSkill
;
1397 // use one of the magic table
1398 if(rootBrick
->ActionNature
==ACTNATURE::CURATIVE_MAGIC
)
1399 sTable
= STCurativeMagic
;
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());
1410 // Forage Prospect: always success
1411 else if(rootBrick
->isForageProspection())
1415 // Forage Extract: false, but return value not used
1416 else if(rootBrick
->isForageExtraction())
1418 skillValue
= pSM
->getSkillValueMaxBranch(rootBrick
->getSkill());
1424 // Default: take skill value of the root
1425 skillValue
= pSM
->getSkillValueMaxBranch(rootBrick
->getSkill());
1426 // well, it's bug time!
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())
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())
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();
1512 if(phrase
.Bricks
.empty())
1514 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
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;
1524 for(i
=0;i
<phrase
.Bricks
.size();i
++)
1526 CSBrickSheet
*brick
= pBM
->getBrick(phrase
.Bricks
[i
]);
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
;
1556 castingTimeFactor
= (float)sumCastTimeCredit
/(float)(sabrinaCost
*sabrinaRelativeCost
);
1558 castingTimeFactor
= (float)sumCastTimeCredit
;
1559 clamp(castingTimeFactor
, 0.f
, 1.f
);
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)
1575 // ***************************************************************************
1576 void CSPhraseManager::getPhraseMagicRange(const CSPhraseCom
&phrase
, uint32 totalActionMalus
, sint
&range
, sint
&rangeMalus
)
1578 CSBrickManager
*pBM
= CSBrickManager::getInstance();
1583 if(phrase
.Bricks
.empty())
1585 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
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;
1595 for(i
=0;i
<phrase
.Bricks
.size();i
++)
1597 CSBrickSheet
*brick
= pBM
->getBrick(phrase
.Bricks
[i
]);
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!!
1624 rangeFactor
= (float)sumRangeCredit
/(float)(sabrinaCost
*sabrinaRelativeCost
);
1626 rangeFactor
= (float)sumRangeCredit
;
1627 clamp(rangeFactor
, 0.f
, 1.f
);
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();
1648 for(uint i
=0;i
<phrase
.Bricks
.size();i
++)
1650 CSBrickSheet
*brick
= pBM
->getBrick(phrase
.Bricks
[i
]);
1653 // test all properties
1654 for(uint j
=0;j
<brick
->Properties
.size();j
++)
1656 if(brick
->Properties
[j
].PropId
==propId
)
1659 sum
+= (float)fabs(brick
->Properties
[j
].Value
);
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"));
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"));
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
);
1692 sum
+= (float)fabs(value
* weight
/ DB_WEIGHT_SCALE
+ value2
);
1694 sum
+= brick
->Properties
[j
].Value
* weight
/ DB_WEIGHT_SCALE
+ brick
->Properties
[j
].Value2
;
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
)
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
])
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
;
1732 bestProba
= _SuccessTableSheet
[st
]->SuccessTable
[i
].PartialSuccessProbability
;
1734 bestProba
= _SuccessTableSheet
[st
]->SuccessTable
[i
].SuccessProbability
;
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 ?
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
)
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
1798 // special for craft (credit is actualy the level of item crafted)
1800 deltaLevel
= skillValue
- min((sint
)creditSum
,(sint
)minMpLevel
);
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
1822 // if the memory slot is different from the action executed
1823 if(_Memories
[_CurrentExecuteLineCycle
].Slot
[_CurrentExecuteSlotCycle
].Id
!= _CurrentExecutePhraseIdCycle
)
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
;
1843 // if yes, verify that the actions under the icons are relevant
1846 // if the memory slot is different from the action executed
1847 if(_Memories
[_CurrentExecuteLineNext
].Slot
[_CurrentExecuteSlotNext
].Id
!= _CurrentExecutePhraseIdNext
)
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
;
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
)
1876 // DisplayCycleSelectionOnActionBar
1877 CInterfaceElement
*viewCycle
= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewCycleAction
);
1880 CInterfaceElement
*ctrl
= NULL
;
1882 ctrl
= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewSlotBase
+ toString(_CurrentExecuteSlotCycle
));
1883 if(displayCycle
&& ctrl
)
1885 viewCycle
->setParentPos(ctrl
);
1886 viewCycle
->setActive(true);
1887 viewCycle
->invalidateCoords();
1891 viewCycle
->setActive(false);
1895 // DisplayNextSelectionOnActionBar
1896 CInterfaceElement
*viewNext
= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewNextAction
);
1899 CInterfaceElement
*ctrl
= NULL
;
1901 ctrl
= CWidgetManager::getInstance()->getElementFromId(PhraseMemoryViewSlotBase
+ toString(_CurrentExecuteSlotNext
));
1902 if(displayNext
&& ctrl
)
1904 viewNext
->setParentPos(ctrl
);
1905 viewNext
->setActive(true);
1906 viewNext
->invalidateCoords();
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
1920 if(_CurrentExecutePhraseIdNext
>0)
1921 nextPhrase
= _CurrentExecutePhraseIdNext
;
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
);
1933 _NextExecuteLeaf
->setValue32(0);
1934 _NextExecuteIsCyclicLeaf
->setValue32(0);
1938 // ***************************************************************************
1939 void CSPhraseManager::clientExecute(uint32 memoryLine
, uint32 memorySlot
, bool cyclic
)
1943 // Inc Local Counter For Server/Client synchronisation
1944 _PhraseCycleExecuteCounter
++;
1945 _PhraseCycleExecuteCounter
&=PHRASE_EXECUTE_COUNTER_MASK
;
1947 _CurrentExecuteLineCycle
= memoryLine
;
1948 _CurrentExecuteSlotCycle
= memorySlot
;
1949 _CurrentExecutePhraseIdCycle
= getMemorizedPhrase(memoryLine
, memorySlot
);
1953 // Inc Local Counter For Server/Client synchronisation
1954 _PhraseNextExecuteCounter
++;
1955 _PhraseNextExecuteCounter
&=PHRASE_EXECUTE_COUNTER_MASK
;
1957 _CurrentExecuteLineNext
= memoryLine
;
1958 _CurrentExecuteSlotNext
= memorySlot
;
1959 _CurrentExecutePhraseIdNext
= getMemorizedPhrase(memoryLine
, memorySlot
);
1962 // update the Action Execution view
1963 updateExecutionDisplay();
1969 _PhraseDebugEndCyclicAction
= T1
+ 8000;
1971 _PhraseDebugEndNextAction
= T1
+ 2000;
1975 // ***************************************************************************
1976 void CSPhraseManager::cancelClientExecute(bool cyclic
)
1980 // Dec Local Counter For Server/Client synchronisation
1981 _PhraseCycleExecuteCounter
--;
1982 _PhraseCycleExecuteCounter
&=PHRASE_EXECUTE_COUNTER_MASK
;
1984 _CurrentExecuteLineCycle
= -1;
1985 _CurrentExecuteSlotCycle
= -1;
1986 _CurrentExecutePhraseIdCycle
= 0;
1990 // Dec Local Counter For Server/Client synchronisation
1991 _PhraseNextExecuteCounter
--;
1992 _PhraseNextExecuteCounter
&=PHRASE_EXECUTE_COUNTER_MASK
;
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();
2013 pIM
->displaySystemInfo("PhraseCycleCasted:"+toString(memoryLine
)+"_"+toString(memorySlot
), "CHK");
2015 pIM
->displaySystemInfo("PhraseNextCasted:"+toString(memoryLine
)+"_"+toString(memorySlot
), "CHK");
2017 extern void debugUpdateActionBar();
2018 debugUpdateActionBar();
2020 // Send the execute to server.
2023 // before, append the execution counter to the list of ACK to wait
2024 appendCurrentToAckExecute(cyclic
);
2030 msg
= "PHRASE:EXECUTE_CYCLIC";
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
);
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
2060 pIM
->displaySystemInfo("PhraseCraftCasted:"+toString(memoryLine
)+"_"+toString(memorySlot
), "CHK");
2061 extern void debugUpdateActionBar();
2062 debugUpdateActionBar();
2064 // Send the execute to the server
2067 // before, append the execution counter to the list of ACK to wait
2068 appendCurrentToAckExecute(false);
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
);
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
2106 pIM
->displaySystemInfo("PhraseCristalize:"+toString(memoryLine
)+"_"+toString(memorySlot
), "CHK");
2107 extern void debugUpdateActionBar();
2108 debugUpdateActionBar();
2112 // before, append the execution counter to the list of ACK to wait
2113 appendCurrentToAckExecute(false);
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
);
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);
2145 // Set the database in local.
2148 IngameDbMngr
.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_MODE
), MBEHAV::COMBAT
); // Mode
2149 UserEntity
->updateVisualProperty(0, CLFECOMMON::PROPERTY_MODE
);
2154 const string msgName
= "COMBAT:DEFAULT_ATTACK";
2156 if(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
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
;
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());
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());
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
)
2208 _AckExecuteCycleList
.clear();
2209 #ifdef PHRASE_DEBUG_VERBOSE
2210 nlinfo("ACK-RESET: CYCLIC.");
2215 _AckExecuteNextList
.clear();
2216 #ifdef PHRASE_DEBUG_VERBOSE
2217 nlinfo("ACK-RESET: NEXT.");
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
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
)
2239 it
->State
= CAckExecuteEntry::OK
;
2240 // erase all elements before it
2241 ackList
.erase(ackList
.begin(), it
);
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());
2251 // **** server cancelation
2254 // try to find the element in list
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
;
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
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;
2288 // if no fallback found (the list contains only KO)
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.
2300 _CurrentExecuteLineCycle
= fallBack
.MemoryLine
;
2301 _CurrentExecuteSlotCycle
= fallBack
.MemorySlot
;
2302 _CurrentExecutePhraseIdCycle
= fallBack
.PhraseId
;
2306 _CurrentExecuteLineNext
= fallBack
.MemoryLine
;
2307 _CurrentExecuteSlotNext
= fallBack
.MemorySlot
;
2308 _CurrentExecutePhraseIdNext
= fallBack
.PhraseId
;
2312 updateExecutionDisplay();
2315 #ifdef PHRASE_DEBUG_VERBOSE
2316 nlinfo("ACK-RECEIVE-KO: %s. counter=%d. size=%d", cyclic
?"CYCLIC":"NEXT", counterValue
, ackList
.size());
2322 // ***************************************************************************
2323 /** Called when the database counter change
2325 class CHandlerPhraseCounterUpdate
: public IActionHandler
2328 virtual void execute (CCtrlBase
* /* pCaller */, const string
&Params
)
2330 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
2334 cyclic
= getParam(Params
, "cyclic") == "1" ;
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;
2350 pPM
->updateExecutionDisplay();
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;
2367 pPM
->updateExecutionDisplay();
2372 REGISTER_ACTION_HANDLER( CHandlerPhraseCounterUpdate
, "phrase_counter_update");
2375 // ***************************************************************************
2376 class CHandlerPhraseDebugClient
: public IActionHandler
2379 virtual void execute (CCtrlBase
* /* pCaller */, const string
&/* Params */)
2383 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2384 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
2386 // simulate server response for Action
2387 if(T1
>pPM
->_PhraseDebugEndNextAction
)
2390 NLGUI::CDBManager::getInstance()->getDbProp(PHRASE_DB_COUNTER_NEXT
)->setValue32(pPM
->_PhraseNextExecuteCounter
);
2392 if(T1
>pPM
->_PhraseDebugEndCyclicAction
)
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)
2417 sint64 tempTick
= serverTick
+ invalidTickTime
;
2420 _EquipInvalidationEnd
= 0;
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
)
2468 if(GenericMsgHeaderMngr
.pushNameToStream("PHRASE:DELETE", out
))
2470 uint16 slotId
= (uint16
)slot
;
2475 nlwarning(" unknown message name 'PHRASE:DELETE");
2479 // ***************************************************************************
2480 void CSPhraseManager::sendLearnToServer(uint32 slot
)
2482 CSPhraseCom phrase
= getPhrase(slot
);
2486 if(!ClientCfg
.Local
)
2489 if(GenericMsgHeaderMngr
.pushNameToStream("PHRASE:LEARN", out
))
2491 uint16 slotId
= (uint16
)slot
;
2497 nlwarning(" unknown message name 'PHRASE:LEARN");
2501 // ***************************************************************************
2502 void CSPhraseManager::fullDelete(uint32 slot
)
2504 // auto-forget all before.
2505 forgetAllThatUsePhrase(slot
);
2510 // send the erase to server
2511 sendDeleteToServer(slot
);
2514 // ***************************************************************************
2515 uint
CSPhraseManager::countAllThatUsePhrase(uint32 slot
)
2520 // for all memory slot
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
)
2537 // ***************************************************************************
2538 void CSPhraseManager::forgetAllThatUsePhrase(uint32 slot
, bool sendMsgOnly
)
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
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
2597 updateAllMemoryCtrlState();
2601 // ***************************************************************************
2602 CSheetId
getRightHandItem()
2604 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
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
2612 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot
-1) +":SHEET", false);
2614 item
= node
->getValue32();
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();
2630 // get the ItemSheet.
2631 CItemSheet
*itemSheet
= dynamic_cast<CItemSheet
*>(SheetMngr
.get(item
));
2633 itemSkill
= itemSheet
->getRequiredSkill();
2637 // Empty Hand => Hand skill
2638 itemSkill
= SKILLS::SFMCAH
;
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();
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
;
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
2680 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot
-1) +":QUALITY", false);
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();
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
2701 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:INVENTORY:BAG:"+toString(itemSlot
-1) +":ENCHANT", false);
2703 ret
= node
->getValue32();
2709 // ***************************************************************************
2710 void CSPhraseManager::updateMemoryCtrlRegenTickRange(uint memorySlot
, CDBCtrlSheet
*ctrl
)
2714 if (ctrl
->isShortCut())
2715 memoryLine
= getSelectedMemoryLineDB();
2717 memoryLine
= getSelectedMemoryAltLineDB();
2720 sint32 phraseId
= getMemorizedPhrase(memoryLine
, memorySlot
);
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
));
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
]);
2746 phraseFlags
|= brick
->BrickRequiredFlags
;
2751 // ***************************************************************************
2752 CTickRange
CSPhraseManager::getRegenTickRange(const CSPhraseCom
&phrase
) const
2754 CTickRange tickRange
;
2757 CSBrickManager
*pBM
= CSBrickManager::getInstance();
2758 // Get the RootBrick of the Phrase
2759 CSBrickSheet
*brick
= pBM
->getBrick(phrase
.Bricks
[0]);
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
)
2778 tickRange
.extend(newTR
);
2790 // ***************************************************************************
2791 NLMISC::TGameCycle
CSPhraseManager::getPowerDisableTime(const CSPhraseCom
&phrase
) const
2793 float regenTime
= 0;
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
]);
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();
2855 // get the slot info
2856 if (ctrl
->isShortCut()) // No memoryLine defined
2857 memoryLine
= getSelectedMemoryLineDB();
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
];
2868 if(ctrl
&& (ctrl
->isSPhraseId() || ctrl
->isMacro()) )
2870 // if the new ctrl is a phrase
2873 // **** if ctrl is macro, change type
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;
2891 const CSPhraseCom
&phrase
= getPhrase(phraseId
);
2892 phraseRootSkill
= getPhraseRootSkill(phrase
.Bricks
);
2895 // Get the RootBrick of the Phrase
2896 CSBrickSheet
*brick
= pBM
->getBrick(phrase
.Bricks
[0]);
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
2913 if( isGlobalMemPhrase
|| pSM
->areSkillOnSameBranch(phraseRootSkill
, itemSkill
) )
2916 // *** Combat: the item must be compatible with the phrase
2917 if( valid
&& isCombatPhrase
&& !skillCompatibleWithCombatPhrase(itemSkill
, getPhrase(phraseId
).Bricks
) )
2920 // *** Power: item enchant.
2921 if( valid
&& isSpecialPowerPhrase
&& !skillCompatibleWithSpecialPowerPhrase(itemSkill
, getPhrase(phraseId
).Bricks
) )
2924 // *** Harvet : not in indoor
2925 if (isHarvestPhrase
&& UserEntity
->indoor())
2928 // **** Faber special: valid only if good ToolType
2929 if(valid
&& itemSkill
== SKILLS::SC
&& isFaberPhrase
)
2933 TOOL_TYPE::TCraftingToolType cttHand
= getRightHandCraftToolType();
2934 // Get The FaberPlan
2935 const CSPhraseCom
&phrase
= getPhrase(phraseId
);
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
)
2947 sint enchantValue
= getRightHandEnchantValue();
2948 // dec because 0 == not enchanted, and 1 == enchanted but no more charge
2950 // if some charge left
2955 // *** check if need an item in hands
2956 if( valid
&& isCombatPhrase
&& !phraseUsableWithEmptyHands( getPhrase(phraseId
).Bricks
) )
2958 if( getRightHandItem() == CSheetId::Unknown
)
2963 // **** special: valid only according to BRICK_TICK_RANGE dbProp.
2966 uint64 currentFlags
;
2967 if (isSpecialPowerPhrase
)
2969 currentFlags
= ~(uint64(0));
2971 for (uint powerIndex
= 32; powerIndex
< 64; ++powerIndex
)
2973 if (!getRegenTickRange(powerIndex
).isEmpty())
2975 currentFlags
&= ~(uint64(1) << powerIndex
);
2981 currentFlags
= uint64(0);
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
)
3005 // **** valid only if the user is not equiping himself
3006 if(isExecutionInvalidCauseEquip())
3011 // Check if the temporary inventory is opened
3012 if(isExecutionInvalidCauseTempInv())
3017 // **** update state
3018 ctrl
->setGrayed(!valid
);
3022 bool neadReadMacro
= false;
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;
3035 if(ctrl
->getMacroId() != macroId
)
3036 neadReadMacro
= true;
3039 if(memSlot
->IsMacroVisualDirty
)
3041 neadReadMacro
= true;
3043 memSlot
->IsMacroVisualDirty
= false;
3049 CMacroCmdManager
*pMM
= CMacroCmdManager::getInstance();
3050 const CMacroCmd
*macroCmd
= pMM
->getMacroFromMacroID(macroId
);
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
++)
3076 CDBCtrlSheet
*ctrl
= dynamic_cast<CDBCtrlSheet
*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryCtrlBase
+ toString(i
)) );
3079 // update the valid state.
3080 updateMemoryCtrlState(i
, ctrl
, itemSkill
);
3082 CDBCtrlSheet
*ctrlAlt
= dynamic_cast<CDBCtrlSheet
*>(CWidgetManager::getInstance()->getElementFromId(PhraseMemoryAltCtrlBase
+ toString(i
)) );
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
)
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
)
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
);
3129 // update the valid state.
3130 updateMemoryCtrlState(memorySlot
, ctrl
, getRightHandItemSkill());
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
);
3146 // update the valid state.
3147 updateMemoryCtrlRegenTickRange(memorySlot
, ctrl
);
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]);
3165 return SKILLS::unknown
;
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
) )
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());
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
))
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
)
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
);
3242 if( !brick
->UsableWithEmptyHands
)
3250 // ***************************************************************************
3251 // ***************************************************************************
3253 // ***************************************************************************
3254 // ***************************************************************************
3256 // ***************************************************************************
3257 void CSPhraseManager::memorizeMacro(uint32 memoryLine
, uint32 memorySlot
, uint32 macroId
)
3259 if(memorySlot
>=PHRASE_MAX_MEMORY_SLOT
)
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
);
3275 _Memories
[memoryLine
].Slot
[memorySlot
].IsMacro
= true;
3276 _Memories
[memoryLine
].Slot
[memorySlot
].Id
= macroId
;
3279 if((sint32
)memoryLine
==_SelectedMemoryDB
)
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
)
3293 // if the slot eihter don't exist, abort
3294 if(memoryLine
>=_Memories
.size())
3296 // if the slot is not a macro
3297 if(!_Memories
[memoryLine
].Slot
[memorySlot
].isMacro())
3300 // update memory. NB: revert to Phrase mode is important
3301 _Memories
[memoryLine
].Slot
[memorySlot
].IsMacro
= false;
3302 _Memories
[memoryLine
].Slot
[memorySlot
].Id
= 0;
3305 if((sint32
)memoryLine
==_SelectedMemoryDB
)
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())
3319 if(memorySlot
>=PHRASE_MAX_MEMORY_SLOT
)
3321 if(!_Memories
[memoryLine
].Slot
[memorySlot
].isMacro())
3324 return _Memories
[memoryLine
].Slot
[memorySlot
].Id
;
3327 // ***************************************************************************
3328 bool CSPhraseManager::isMemorizedMacro(uint32 memoryLine
, uint32 memorySlot
) const
3330 if(memoryLine
>=_Memories
.size())
3332 if(memorySlot
>=PHRASE_MAX_MEMORY_SLOT
)
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
)
3372 // ***************************************************************************
3373 struct CMacroMemSerialSlot
3379 void serial(NLMISC::IStream
&f
)
3381 f
.serial(MemoryLine
);
3382 f
.serial(MemorySlot
);
3387 void CSPhraseManager::serialMacroMemory(NLMISC::IStream
&f
)
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
);
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
;
3423 slot
.MacroId
= memSlot
.Id
;
3424 macroList
.push_back(slot
);
3430 f
.serialCont(macroList
);
3435 // ***************************************************************************
3436 // ***************************************************************************
3438 // ***************************************************************************
3439 // ***************************************************************************
3442 // ***************************************************************************
3443 uint32
CSPhraseManager::getCurrentPhraseSheetPrice(uint32 phraseSheetId
)
3445 CSBrickManager
*pBM
= CSBrickManager::getInstance();
3449 // get the SPhraseSheet
3450 CSPhraseSheet
*phraseSheet
= dynamic_cast<CSPhraseSheet
*>(SheetMngr
.get(CSheetId(phraseSheetId
)));
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
]);
3462 price
+= brick
->SPCost
;
3471 // ***************************************************************************
3472 uint32
CSPhraseManager::getBasePhraseSheetPrice(uint32 phraseSheetId
)
3474 CSBrickManager
*pBM
= CSBrickManager::getInstance();
3478 // get the SPhraseSheet
3479 CSPhraseSheet
*phraseSheet
= dynamic_cast<CSPhraseSheet
*>(SheetMngr
.get(CSheetId(phraseSheetId
)));
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
]);
3489 price
+= brick
->SPCost
;
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());
3511 _BotChatPhrasePriceLeaves
[i
]->setValue32(price
);
3517 // ***************************************************************************
3518 // ***************************************************************************
3520 // ***************************************************************************
3521 // ***************************************************************************
3524 // ***************************************************************************
3525 void CSPhraseManager::updatePhraseProgressionDB()
3527 // If DB not inited, no-op
3528 if(!_InitInGameDone
)
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();
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
3551 if(isPhraseCastable(ppi
.SheetId
))
3552 progressType
= ActionProgress
;
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
)
3565 // is this phrase known?
3567 CSPhraseSheet
*phrase
= dynamic_cast<CSPhraseSheet
*>(SheetMngr
.get(NLMISC::CSheetId(ppi
.SheetId
)));
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
]))
3580 // if show, but only if full learnt, skip it if not fully learnt
3581 if (phrase
->ShowInAPOnlyIfLearnt
&& !known
)
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
);
3595 _ProgressionDbLocks
[progressType
][dbIndex
]->setValue32(0);
3598 // just gray if all requirement are met in order to learn it
3599 if(phraseRequirementOk(ppi
.SheetId
))
3600 _ProgressionDbLocks
[progressType
][dbIndex
]->setValue32(1);
3603 _ProgressionDbLocks
[progressType
][dbIndex
]->setValue32(2);
3606 // increment the db index
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);
3620 _LastProgressionNumDbFill
[pt
]= progressIndex
[pt
];
3625 // ***************************************************************************
3626 class CPhraseSortEntry
3629 CSPhraseManager::CPhraseProgressInfo ProgressInfo
;
3637 bool operator<(const CPhraseSortEntry
&pse
) const
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
;
3647 return Type
<pse
.Type
;
3648 // sort by Icon (same type possible for effect bricks)
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();
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());
3681 // if not shown in ActionProgression window, skip it
3682 if(!phrase
->ShowInActionProgression
)
3685 // if not valid, skip it
3686 if(!phrase
->isValid())
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
3694 for(i
=0;i
<phrase
->Bricks
.size();i
++)
3696 CSBrickSheet
*brick
= pBM
->getBrick(phrase
->Bricks
[i
]);
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
++)
3706 sv
.Skill
= brick
->RequiredSkills
[j
].Skill
;
3707 sv
.Value
= brick
->RequiredSkills
[j
].Value
;
3708 brickFormula
.orV(sv
);
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");
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
)
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
3798 std::vector
<CPhraseSortEntry
> phraseSortEntry
;
3799 for(uint i
=0;i
<SKILLS::NUM_SKILLS
;i
++)
3801 CPhraseProgression
&pprog
= _ProgressionPhrases
[i
];
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
;
3815 brickSheet
= pBM
->getSabrinaCom().getPhraseBestDisplayBrick(phraseSheet
->Bricks
);
3817 // take first the castable phrase
3818 pse
.Castable
= isPhraseCastable(phraseSheet
);
3821 pse
.Section
= getPhraseSectionFromLevel(pse
.ProgressInfo
.Level
);
3823 // get the family of this brick as second sort key
3824 CSBrickSheet
*brick
= pBM
->getBrick(brickSheet
);
3826 pse
.Type
= brick
->BrickFamily
;
3830 // get the icon of this brick as third sort key
3832 pse
.Icon
= (uint32
)brick
->IdIcon
;
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
]))
3849 // get the whole number
3850 for(;k
<pse
.Text
.size() && (uint8
)pse
.Text
[k
] < (uint8
)'\x80' && isdigit(pse
.Text
[k
]);k
++)
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();
3865 sort(phraseSortEntry
.begin(), phraseSortEntry
.end());
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
)
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
3904 skillReqLevel
[skill
]= max(skillReqLevel
[skill
], (sint
)value
);
3907 // else recurs to children
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));
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();
3940 CSPhraseSheet
*phraseSheet
= dynamic_cast<CSPhraseSheet
*>(SheetMngr
.get(CSheetId(phraseSheetId
)));
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
]);
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
]);
3961 // Special case: For "Use Item enchantment" phrase, don't describe requirement
3962 if(brick
->isProcEnchantment())
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
)));
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
]);
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
]);
4012 // get player fame for this faction and check with min value needed
4013 if( brick
->FactionIndex
!= CStaticFames::INVALID_FACTION_INDEX
)
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())
4044 // **** check fame requirement
4045 if( !fameRequirementOk(phraseSheetId
) )
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
]) )
4064 // ok all requirement met
4069 // ***************************************************************************
4070 bool CSPhraseManager::allowListBrickInHelp(const CSPhraseCom
&phrase
) const
4072 CSBrickManager
*pBM
= CSBrickManager::getInstance();
4074 CSBrickSheet
*rootBrick
= pBM
->getBrick(phrase
.Bricks
[0]);
4079 // don't list bricks for Charac upgrades
4080 if(BRICK_FAMILIES::isCharacBuyFamily(rootBrick
->BrickFamily
))
4083 // don't list bricks for "use item enchant" phrase
4084 if(rootBrick
->isProcEnchantment())
4091 // ***************************************************************************
4092 void CSPhraseManager::getCombatWeaponRestriction(string
&text
, const CSPhraseCom
&phrase
, bool& usableWithMelee
, bool& usableWithRange
)
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());
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
]);
4135 if(i
<skills
.size()-1)
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
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
)
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();
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
);
4222 // ***************************************************************************
4223 void CSPhraseManager::getResistMagic(bool resistMagic
[RESISTANCE_TYPE::NB_RESISTANCE_TYPE
], const std::vector
<NLMISC::CSheetId
> &phraseBricks
)
4226 for(uint i
=0;i
<RESISTANCE_TYPE::NB_RESISTANCE_TYPE
;i
++)
4228 resistMagic
[i
]= false;
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
);
4253 // if this is the last referenced, then return this phrase id
4254 if(countAllThatUsePhrase(oldPhraseId
)==1)
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
4272 // more than 1 use this slot? => no problem (will be forgeted/replaced)
4273 if(countAllThatUsePhrase(phraseId
)>1)
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
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())
4324 // **** Search in all memory lines, but try the currently selected one
4325 std::deque
<uint
> memoryLineSort
;
4326 memoryLineSort
.resize(_Memories
.size());
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() )
4354 memoryLine
= memoryLineSort
[i
];
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
)
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();
4396 // not initialized => abort
4397 if(_BotChatPhraseSheetLeaves
.empty())
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())
4411 // ShowInAPOnlyIfLearnt are typically special mission phrase, skip them
4412 if(phrase
->ShowInAPOnlyIfLearnt
)
4414 // test rmFlags against phrase name
4415 if( !ROLEMASTER_FLAGS::canSellPhrase(rmFlags
, phrase
->Id
.toString()) )
4417 // test race (test all bricks)
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
)
4432 // if some brick failed, skip
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
]))
4449 // if all bricks of this phrase are known, then don't need to sell!
4454 // *** Filter phrases that are ok with requirement
4455 if(!phraseRequirementOk(phrase
->Id
.asInt()))
4459 // *** All filter pass, add this phrase
4460 if(_BotChatPhraseSheetLeaves
[destIndex
])
4461 _BotChatPhraseSheetLeaves
[destIndex
]->setValue32(phrase
->Id
.asInt());
4466 if(destIndex
==_BotChatPhraseSheetLeaves
.size())
4474 // *** Fill others with 0!
4475 for(;destIndex
<_BotChatPhraseSheetLeaves
.size();destIndex
++)
4477 if(_BotChatPhraseSheetLeaves
[destIndex
])
4478 _BotChatPhraseSheetLeaves
[destIndex
]->setValue32(0);
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;
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]);
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);
4539 // ***************************************************************************
4540 CTickRange
CSPhraseManager::getRegenTickRange(uint powerIndex
) const
4542 CTickRange tickRange
;
4543 CCDBNodeLeaf
*dbLeaf
= getRegenTickRangeDbLeaf(powerIndex
);
4546 tickRange
.StartTick
= (NLMISC::TGameCycle
) dbLeaf
->getValue32();
4547 tickRange
.EndTick
= (NLMISC::TGameCycle
) (((uint64
) dbLeaf
->getValue64()) >> 32);
4552 // ***************************************************************************
4553 void CSPhraseManager::setRegenTickRange(uint powerIndex
, const CTickRange
&tickRange
)
4555 CCDBNodeLeaf
*dbLeaf
= getRegenTickRangeDbLeaf(powerIndex
);
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())
4572 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4574 float castTimeMalus
;
4575 pPM
->getPhraseCastTime(Phrase
, pPM
->getTotalActionMalus(Phrase
), castTime
, castTimeMalus
);
4576 ls
.push(castTime
+ castTimeMalus
);
4580 // ***************************************************************************
4581 int CSPhraseComAdpater::luaGetCastRange(CLuaState
&ls
)
4583 if (Phrase
.Bricks
.empty())
4588 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4591 pPM
->getPhraseMagicRange(this->Phrase
, pPM
->getTotalActionMalus(Phrase
), range
, rangeMalus
);
4592 ls
.push(range
+ rangeMalus
);
4596 // ***************************************************************************
4597 int CSPhraseComAdpater::luaGetHpCost(CLuaState
&ls
)
4599 if (Phrase
.Bricks
.empty())
4604 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4607 pPM
->getPhraseHpCost(this->Phrase
, pPM
->getTotalActionMalus(Phrase
), hpCost
, hpCostMalus
);
4608 ls
.push(hpCost
+ hpCostMalus
);
4612 // ***************************************************************************
4613 int CSPhraseComAdpater::luaGetSapCost(CLuaState
&ls
)
4615 if (Phrase
.Bricks
.empty())
4620 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4623 pPM
->getPhraseSapCost(this->Phrase
, pPM
->getTotalActionMalus(Phrase
), sapCost
, sapCostMalus
);
4624 ls
.push(sapCost
+ sapCostMalus
);
4628 // ***************************************************************************
4629 int CSPhraseComAdpater::luaGetSuccessRate(CLuaState
&ls
)
4631 if (Phrase
.Bricks
.empty())
4636 CSPhraseManager
*pPM
= CSPhraseManager::getInstance(); ;
4637 ls
.push(pPM
->getPhraseSuccessRate(this->Phrase
));
4642 // ***************************************************************************
4643 int CSPhraseComAdpater::luaGetFocusCost(CLuaState
&ls
)
4645 if (Phrase
.Bricks
.empty())
4650 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4652 sint focusCostMalus
;
4653 pPM
->getPhraseFocusCost(this->Phrase
, pPM
->getTotalActionMalus(Phrase
), focusCost
, focusCostMalus
);
4654 ls
.push(focusCost
+ focusCostMalus
);
4658 // ***************************************************************************
4659 int CSPhraseComAdpater::luaGetStaCost(CLuaState
&ls
)
4661 if (Phrase
.Bricks
.empty())
4666 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4669 pPM
->getPhraseStaCost(this->Phrase
, pPM
->getTotalActionMalus(Phrase
), staCost
, staCostMalus
);
4670 ls
.push(staCost
+ staCostMalus
);
4675 // ***************************************************************************
4676 int CSPhraseComAdpater::luaGetName(CLuaState
&ls
)
4678 #ifdef RYZOM_LUA_UCSTRING
4679 CLuaIHM::push(ls
, this->Phrase
.Name
);
4681 ls
.push(this->Phrase
.Name
.toUtf8());
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
));
4698 #ifdef RYZOM_LUA_UCSTRING
4699 ucstring uc
= ucstring::makeFromUtf8(desc
); // Compatibility
4700 CLuaIHM::push(ls
, uc
);
4710 // ***************************************************************************
4711 int CSPhraseComAdpater::luaIsCombatPhrase(CLuaState
&ls
)
4718 CSBrickManager
*pBM
= CSBrickManager::getInstance();
4719 CSBrickSheet
*rootBrick
= pBM
->getBrick(Phrase
.Bricks
[0]);
4725 ls
.push(rootBrick
->isCombat());
4729 // ***************************************************************************
4730 int CSPhraseComAdpater::luaIsMagicPhrase(CLuaState
&ls
)
4737 CSBrickManager
*pBM
= CSBrickManager::getInstance();
4738 CSBrickSheet
*rootBrick
= pBM
->getBrick(Phrase
.Bricks
[0]);
4744 ls
.push(rootBrick
->isMagic());
4748 // ***************************************************************************
4749 int CSPhraseComAdpater::luaIsPowerPhrase(CLuaState
&ls
)
4756 CSBrickManager
*pBM
= CSBrickManager::getInstance();
4757 CSBrickSheet
*rootBrick
= pBM
->getBrick(Phrase
.Bricks
[0]);
4763 ls
.push(rootBrick
->isSpecialPower());
4767 // ***************************************************************************
4768 int CSPhraseComAdpater::luaGetRegenTime(CLuaState
&ls
)
4770 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4771 ls
.push((sint
)pPM
->getRegenTime(Phrase
));
4775 // ***************************************************************************
4776 int CSPhraseComAdpater::luaGetTotalRegenTime(CLuaState
&ls
)
4778 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4779 ls
.push((sint
)pPM
->getTotalRegenTime(Phrase
));
4783 // ***************************************************************************
4784 int CSPhraseComAdpater::luaGetPowerDisableTime(CLuaState
&ls
)
4786 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4787 ls
.push((sint
)pPM
->getPowerDisableTime(Phrase
));
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();
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
);
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();
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();