1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 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) 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/>.
23 // ***************************************************************************
26 #include "group_skills.h"
27 #include "interface_manager.h"
28 #include "nel/gui/interface_expr.h"
30 #include "nel/gui/view_text.h"
31 #include "nel/gui/view_bitmap.h"
32 #include "nel/gui/dbview_number.h"
33 #include "nel/gui/dbview_bar.h"
35 #include "game_share/skills.h"
36 #include "nel/misc/xml_auto_ptr.h"
38 #include "skill_manager.h"
39 #include "nel/misc/i_xml.h"
41 #include "../string_manager_client.h"
43 #include "../user_entity.h"
45 // ***************************************************************************
47 using namespace NLMISC
;
49 // ***************************************************************************
50 #define DB_SKILLS "SERVER:CHARACTER_INFO:SKILLS"
51 #define WIN_TREE_LIST "sbtree:tree_list"
53 extern CUserEntity
*UserEntity
;
56 extern void contextHelp (const std::string
&help
);
58 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupSkills
, std::string
, "skills_displayer");
61 bool CGroupSkills::InhibitSkillUpFX
= true;
64 // ***************************************************************************
65 bool CGroupSkills::parse (xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
67 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
69 if(!CInterfaceGroup::parse(cur
, parentGroup
))
74 // read _TemplateSkill
75 ptr
= (char*) xmlGetProp (cur
, (xmlChar
*)"template_skill");
78 _TemplateSkill
= (const char*)ptr
;
82 ptr
= (char*) xmlGetProp (cur
, (xmlChar
*)"node_handler");
85 _AHCtrlNode
= (const char*)ptr
;
89 // Add observer on the skill parameter because if the skill is coming to be not-zero, a new skill must be displayed
90 _SkillsObs
.Owner
= this;
93 ICDBNode::CTextId textId
;
95 for (uint k
= 0; k
< SKILLS::NUM_SKILLS
; ++k
)
97 sTmp
= string(DB_SKILLS
)+":"+NLMISC::toString((sint32
)k
)+":BaseSKILL";
98 textId
= ICDBNode::CTextId( sTmp
);
99 NLGUI::CDBManager::getInstance()->getDB()->addObserver (&_SkillsObs
, textId
);
104 // create all the Tree Nodes now
105 createAllTreeNodes();
110 // ***************************************************************************
111 void CGroupSkills::clearGroups()
113 CInterfaceGroup::clearGroups();
116 // ***************************************************************************
117 void CGroupSkills::checkCoords ()
121 CInterfaceGroup::checkCoords();
125 // ***************************************************************************
126 void CGroupSkills::rebuild()
128 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
129 CSkillManager
*pSM
= CSkillManager::getInstance();
131 // **** first time bind?
134 _Tree
= dynamic_cast<CGroupTree
*>(CWidgetManager::getInstance()->getElementFromId(getId(),WIN_TREE_LIST
));
137 nlwarning("cant find tree");
142 _Tree
->setRootNode (_TreeRoot
);
143 _Tree
->selectLine(0);
146 // **** Update the Show flag of each node
148 for (uint32 i
= 0; i
< SKILLS::NUM_SKILLS
; ++i
)
151 if (!pSM
->isUnknown((SKILLS::ESkills
)i
))
153 // Show level 0 and level 1 skills + skills that have a PARENT with value > 0
154 SKILLS::ESkills parentSkill
= pSM
->getParent((SKILLS::ESkills
)i
);
156 // if no grand parent, or if grand parent trained
157 if (pSM
->getParent(parentSkill
) == SKILLS::unknown
||
158 pSM
->getBaseSkillValue(parentSkill
) > 0)
164 _AllNodes
[i
]->Show
= show
;
168 _Tree
->forceRebuild();
171 _MustRebuild
= false;
175 // ***************************************************************************
177 void CGroupSkills::CSkillsObs::update (ICDBNode
*node
)
179 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
181 CCDBNodeLeaf
*leaf
= NLMISC::safe_cast
<CCDBNodeLeaf
*>(node
);
183 // Rebuild all only if new skill (previously level 0)
184 if (leaf
->getOldValue32() == 0)
185 Owner
->_MustRebuild
= true;
187 // Popup a message if previous was not 0
188 if ((leaf
->getOldValue32() != 0) &&
189 (leaf
->getValue32() != 0)) // prevent displaying FX when resetData() is called during a Far TP
191 CGroupSkills::InhibitSkillUpFX
= false;
193 ICDBNode
*skill
= node
->getParent();
196 ICDBNode
*skillParent
= skill
->getParent();
200 if (skillParent
->getNodeIndex (skill
, skillId
))
202 CAHManager::getInstance()->runActionHandler("skill_popup", NULL
, "skillId="+toString(skillId
)+"|delta="+toString(leaf
->getValue32()-leaf
->getOldValue32()));
205 contextHelp ("skill");
212 if( !CGroupSkills::InhibitSkillUpFX
) // TODO: couldn't this be replaced by IngameDbMngr.initInProgress()?
214 UserEntity
->skillUp();
218 // Check if this skill canunblock title
219 CSkillManager
*pSM
= CSkillManager::getInstance();
220 string sTmp
= leaf
->getFullName();
221 sTmp
= sTmp
.substr(0, sTmp
.rfind(':'));
222 sTmp
= sTmp
.substr(sTmp
.rfind(':')+1,sTmp
.size());
224 fromString(sTmp
, eSkills
);
225 pSM
->tryToUnblockTitleFromSkill((SKILLS::ESkills
)eSkills
, leaf
->getValue32());
229 // ***************************************************************************
230 // Get the skill buf text
231 static DECLARE_INTERFACE_USER_FCT(getSkillBaseText
)
233 if (args
.size() != 2)
235 nlwarning("<getSkillBaseText> Expecting 2 arg.");
238 if (!args
[0].toInteger() && !args
[1].toInteger())
240 nlwarning("<getSkillBaseText> Can't convert arg 0/1 to a int value.");
244 sint64 skillValue
= args
[0].getInteger();
245 sint64 skillBase
= args
[1].getInteger();
247 if(skillValue
!=skillBase
)
249 result
.setString( toString("(%d)", skillBase
) );
253 result
.setString( std::string() );
258 REGISTER_INTERFACE_USER_FCT("getSkillBaseText", getSkillBaseText
);
261 // ***************************************************************************
265 CGroupTree::SNode
*Node
;
268 bool operator<(const CSortNode
&o
) const
270 return Value
<o
.Value
;
274 CGroupSkills::CGroupSkills( const TCtorParam
¶m
) :
275 CInterfaceGroup(param
)
277 _MustRebuild
= false;
283 CGroupSkills::~CGroupSkills()
286 _SkillsObs
.Owner
= NULL
;
288 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
290 ICDBNode::CTextId textId
;
291 for (uint k
= 0; k
< SKILLS::NUM_SKILLS
; ++k
)
293 sTmp
= string(DB_SKILLS
)+":"+NLMISC::toString((sint32
)k
)+":BaseSKILL";
294 textId
= ICDBNode::CTextId( sTmp
);
295 NLGUI::CDBManager::getInstance()->getDB()->removeObserver(&_SkillsObs
, textId
);
298 // first remove any nodes from the tree group
301 // reset now the node hierarchy. NB: the node hierarchy is also deleted
302 _Tree
->setRootNode(NULL
);
306 // template nodes not linked to hierarchy will memory leak, we must remove them also
307 for (sint i
= 0; i
<SKILLS::NUM_SKILLS
; i
++)
309 // is the refptr still allocated?
312 // NB: delete call makeOrphan, and delete children that may still be in the array
313 // but it's OK, because tested with refptr
319 // still allocated (refptr) ?
326 void CGroupSkills::createAllTreeNodes()
328 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
329 CSkillManager
*pSM
= CSkillManager::getInstance();
331 // Construct the snode hierarchy structure
332 _TreeRoot
= new CGroupTree::SNode
;
333 _AllNodes
.resize(SKILLS::NUM_SKILLS
, NULL
);
337 // local variable (avoid realloc in loop)
338 vector
< pair
<string
, string
> > tempVec(2);
339 const char *sSkillName
;
341 while ((!bQuit
) && (nCounter
< 32)) // Counter is used to not infinitly loop
345 // Try to create a skill
346 for (uint32 i
= 0; i
< SKILLS::NUM_SKILLS
; ++i
)
347 if (_AllNodes
[i
] == NULL
) // not already created
349 if (pSM
->isUnknown((SKILLS::ESkills
)i
)) continue;
352 SKILLS::ESkills parentSkill
= pSM
->getParent((SKILLS::ESkills
)i
);
354 // if parent, the parent node must be created
355 if (parentSkill
!= SKILLS::unknown
)
357 if (_AllNodes
[parentSkill
] == NULL
)
365 CGroupTree::SNode
*pNode
= new CGroupTree::SNode
;
366 pNode
->Id
= NLMISC::toString(i
);
369 sSkillName
= STRING_MANAGER::CStringManagerClient::getSkillLocalizedName((SKILLS::ESkills
)i
);
371 // just text or template?
372 if(_TemplateSkill
.empty())
374 pNode
->DisplayText
= true;
375 pNode
->Template
= NULL
;
376 pNode
->Text
= sSkillName
;
380 pNode
->DisplayText
= false;
382 // create the template
383 tempVec
[0].first
="id"; tempVec
[0].second
= pNode
->Id
;
384 tempVec
[1].first
="skillid"; tempVec
[1].second
= NLMISC::toString(i
);
385 CInterfaceGroup
*pIG
= CWidgetManager::getInstance()->getParser()->createGroupInstance(_TemplateSkill
, getId() + ":" + WIN_TREE_LIST
, tempVec
);
389 CViewText
*pViewSkillName
= dynamic_cast<CViewText
*>(pIG
->getView("name"));
390 if (pViewSkillName
!= NULL
)
391 pViewSkillName
->setText (sSkillName
);
392 // Set Skill Max Value
393 CViewText
*pViewSkillMax
= dynamic_cast<CViewText
*>(pIG
->getView("max"));
394 if (pViewSkillMax
!= NULL
)
395 pViewSkillMax
->setText (toString(pSM
->getMaxSkillValue((SKILLS::ESkills
)i
)));
396 pNode
->Template
= pIG
;
401 if(!_AHCtrlNode
.empty())
403 pNode
->AHName
= _AHCtrlNode
;
404 pNode
->AHParams
= NLMISC::toString(i
);
408 _AllNodes
[i
] = pNode
;
410 // not opened by default
411 pNode
->Opened
= false;
413 // Attach to the good parent
414 if (parentSkill
== SKILLS::unknown
)
415 _TreeRoot
->addChild(pNode
);
417 _AllNodes
[parentSkill
]->addChild(pNode
);
421 // Sort the First level in this order: Combat/Magic/Craft/Forage/Others.
422 vector
<CSortNode
> sortNodes
;
423 sortNodes
.resize(_TreeRoot
->Children
.size());
425 for(i
=0;i
<_TreeRoot
->Children
.size();i
++)
427 sortNodes
[i
].Node
= _TreeRoot
->Children
[i
];
428 // get the skill value of this node
430 fromString(_TreeRoot
->Children
[i
]->Id
, skillValue
);
432 if(skillValue
==SKILLS::SF
)
434 if(skillValue
==SKILLS::SM
)
436 if(skillValue
==SKILLS::SC
)
438 if(skillValue
==SKILLS::SH
)
441 sortNodes
[i
].Value
= skillValue
;
443 sort(sortNodes
.begin(), sortNodes
.end());
444 // store sorted values
445 for(i
=0;i
<_TreeRoot
->Children
.size();i
++)
447 _TreeRoot
->Children
[i
]= sortNodes
[i
].Node
;