Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / client_sheets / sbrick_sheet.cpp
blob5f3f0c1495487a8189f089f87cc0757b23e59fa5
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "sbrick_sheet.h"
24 #include "nel/georges/u_form_elm.h"
25 #include "nel/misc/common.h"
26 #include "nel/misc/algo.h"
27 #include "item_sheet.h"
28 #include "game_share/brick_flags.h"
30 using namespace std;
31 using namespace NLMISC;
32 using namespace NLGEORGES;
35 // ***************************************************************************
36 // Easy Macro to translate .typ enum
37 #define TRANSLATE_ENUM( _Var_, _unknown_, _func_, _key_) \
38 _Var_ = _unknown_; \
39 if( !root.getValueByName(val, _key_)) \
40 debug("Key '" _key_ "' not found."); \
41 else if( (_Var_ = _func_(val)) == _unknown_ ) \
42 debug(#_Var_ " Unknown: " + val);
44 // Same but no error if the result of enum is _unknown_
45 #define TRANSLATE_ENUM_NODB( _Var_, _unknown_, _func_, _key_) \
46 _Var_ = _unknown_; \
47 if( !root.getValueByName(val, _key_)) \
48 debug( toString("Key '%s' not found.", _key_) ); \
49 else \
50 _Var_ = _func_(val);
53 // Easy macro to translate value from georges
54 #define TRANSLATE_VAL( _Var_, _key_ ) \
55 if(!root.getValueByName(_Var_, _key_)) \
56 debug( string("key '") + string(_key_) + string("' not found.") );
59 // ***************************************************************************
60 static void strRemoveChar(string &str, char c)
62 str.erase( remove(str.begin(), str.end(), c), str.end() );
65 // ***************************************************************************
66 void CSBrickSheet::build (const NLGEORGES::UFormElm &root)
68 string sTmp;
69 uint i;
71 // read the array of skills
72 string skillUseStr;
73 TRANSLATE_VAL(skillUseStr, "Basics.Skill");
74 while(strFindReplace(skillUseStr, ":", " "));
75 std::vector<string> listSkill;
76 splitString(skillUseStr," ",listSkill);
77 // build the req skill array
78 UsedSkills.clear();
79 UsedSkills.reserve(listSkill.size());
80 for(i=0;i<listSkill.size();i++)
82 SKILLS::ESkills skill= SKILLS::toSkill(listSkill[i]);
83 // Yoyo: patch to read auto generated bricks
84 if(skill==SKILLS::unknown)
86 skill = (SKILLS::ESkills)SKILLS::toSkill("S" + listSkill[i]);
88 // keep only whats work
89 if(skill!=SKILLS::unknown)
91 UsedSkills.push_back(skill);
94 // if empty, fill at least with unknown
95 if(UsedSkills.empty())
96 UsedSkills.push_back(SKILLS::unknown);
98 // get family id
99 root.getValueByName (sTmp, "Basics.FamilyId" );
100 BrickFamily = BRICK_FAMILIES::toSBrickFamily (sTmp);
101 if(BrickFamily==BRICK_FAMILIES::Unknown)
102 nlwarning("CSBrickSheet:build: BrickFamily Unknown '%s'.", sTmp.c_str());
104 // Yoyo: patch to read auto generated bricks
105 if(BrickFamily==BRICK_FAMILIES::Unknown)
107 string sheetName= Id.toString();
108 std::string::size_type end= sheetName.find(".sbrick")-2;
109 BrickFamily = BRICK_FAMILIES::toSBrickFamily ( NLMISC::toUpperAscii(sheetName.substr(0,end)) );
110 if(BrickFamily==BRICK_FAMILIES::Unknown)
111 nlwarning("Unknown Family for SBrick: %s", sheetName.c_str());
114 root.getValueByName (IndexInFamily, "Basics.IndexInFamily" );
115 root.getValueByName (Level, "Basics.Level" );
116 // read icons
117 string Icon;
118 root.getValueByName (Icon, "Client.Icon" );
119 Icon = toLowerAscii(Icon);
120 IdIcon = ClientSheetsStrings.add(Icon);
122 string IconBack;
123 root.getValueByName (IconBack, "Client.IconBack" );
124 IconBack = toLowerAscii(IconBack);
125 IdIconBack = ClientSheetsStrings.add(IconBack);
127 string IconOver;
128 root.getValueByName (IconOver, "Client.IconOver" );
129 IconOver = toLowerAscii(IconOver);
130 IdIconOver = ClientSheetsStrings.add(IconOver);
132 string IconOver2;
133 root.getValueByName (IconOver2, "Client.IconOver2" );
134 IconOver2 = toLowerAscii(IconOver2);
135 IdIconOver2 = ClientSheetsStrings.add(IconOver2);
137 root.getValueByName (IconColor, "Client.IconColor" );
138 root.getValueByName (IconBackColor, "Client.IconBackColor");
139 root.getValueByName (IconOverColor, "Client.IconOverColor");
140 root.getValueByName (IconOver2Color, "Client.IconOver2Color");
141 root.getValueByName (SabrinaCost, "Basics.SabrinaCost" );
142 root.getValueByName (SabrinaRelativeCost, "Basics.SabrinaRelativeValue" );
144 // mandatory families
145 MandatoryFamilies.clear();
146 char tmp[256];
147 for(i=0;i<SBRICK_MAX_MANDATORY;i++)
149 sprintf(tmp, "Mandatory.f%d", i);
150 root.getValueByName (sTmp, tmp);
151 if (!sTmp.empty())
153 BRICK_FAMILIES::TBrickFamily bf= BRICK_FAMILIES::toSBrickFamily(sTmp);
154 if(bf != BRICK_FAMILIES::Unknown)
155 MandatoryFamilies.push_back( bf );
156 else
157 nlwarning("Unknown Mandatory family %s",sTmp.c_str());
161 // Optional families
162 OptionalFamilies.clear();
163 for(i=0;i<SBRICK_MAX_OPTIONAL;i++)
165 sprintf(tmp, "Optional.f%d", i);
166 root.getValueByName (sTmp, tmp);
167 if (!sTmp.empty())
169 BRICK_FAMILIES::TBrickFamily bf= BRICK_FAMILIES::toSBrickFamily(sTmp);
170 if(bf != BRICK_FAMILIES::Unknown)
171 OptionalFamilies.push_back( bf );
172 else
173 nlwarning("Unknown optional family %s",sTmp.c_str());
177 // Parameter families
178 ParameterFamilies.clear();
179 for(i=0;i<SBRICK_MAX_PARAMETER;i++)
181 sprintf(tmp, "Parameter.f%d", i);
182 root.getValueByName (sTmp, tmp);
183 if (!sTmp.empty())
185 BRICK_FAMILIES::TBrickFamily bf= BRICK_FAMILIES::toSBrickFamily(sTmp);
186 if(bf != BRICK_FAMILIES::Unknown)
187 ParameterFamilies.push_back( bf );
188 else
189 nlwarning("Unknown Parameter family %s",sTmp.c_str());
193 // Credit families
194 CreditFamilies.clear();
195 for(i=0;i<SBRICK_MAX_CREDIT;i++)
197 sprintf(tmp, "Credit.f%d", i);
198 root.getValueByName (sTmp, tmp);
199 BRICK_FAMILIES::TBrickFamily bf= BRICK_FAMILIES::toSBrickFamily(sTmp);
200 if(bf != BRICK_FAMILIES::Unknown)
201 CreditFamilies.push_back( bf );
204 string ForbiddenDef;
205 root.getValueByName (ForbiddenDef, "Basics.ForbiddenDef" );
206 IdForbiddenDef = ClientSheetsStrings.add(ForbiddenDef);
208 string ForbiddenExclude;
209 root.getValueByName (ForbiddenExclude, "Basics.ForbiddenExclude" );
210 IdForbiddenExclude = ClientSheetsStrings.add(ForbiddenExclude);
212 // **** Properties
213 Properties.clear();
214 for(i=0;i<MaxProperties;i++)
216 string val;
217 root.getValueByName(val, toString("Basics.Property %d", i).c_str() );
218 if(!val.empty() && val!="NULL")
220 CProperty prop;
221 prop.Text= val;
222 Properties.push_back(prop);
226 // **** Faber
227 // The FaberPlan are stored in Mandatory only, but the tool filter is in root
228 if( isFaber() && (isMandatory() || isRoot()) )
230 string val;
232 FaberPlan.ItemPartMps.clear();
233 FaberPlan.FormulaMps.clear();
235 // Get Item Built
236 TRANSLATE_VAL(val, "faber.Create.Crafted Item");
237 FaberPlan.ItemBuilt.buildSheetId(val);
239 // Get Skill Filters
240 TRANSLATE_ENUM ( FaberPlan.ToolType, TOOL_TYPE::Unknown, TOOL_TYPE::toToolType, "faber.Tool type");
242 // Get NB item built (for ammo)
243 TRANSLATE_VAL( FaberPlan.NbItemBuilt, "faber.Create.Nb built items");
245 // MPs. Try all MP1 .. 5 slots. Stop when not valid
246 for (uint k=0; k< MAX_FABER_REQ_MP; ++k)
248 sint32 mpQuantity= 0;
249 root.getValueByName(mpQuantity, toString("faber.Create.Quantity %d", k+1).c_str() );
250 // if the req quantity is not 0
251 if ( mpQuantity>0 )
253 CFaberPlan::CItemPartMP mpVal;
254 mpVal.Quantity= mpQuantity;
255 // No error if unknown: filter not used (all MPs match)
256 TRANSLATE_ENUM_NODB( mpVal.FaberTypeFilter, RM_FABER_TYPE::Unknown, RM_FABER_TYPE::toFaberType,
257 toString("faber.Create.MP %d", k+1).c_str() );
259 // Add this req MP.
260 FaberPlan.ItemPartMps.push_back(mpVal);
262 // else subsequents MP slots are not used
263 else
264 break;
267 // Formula MPs. Try all MP1 .. 5 slots. Stop when not valid
268 for (uint k=0; k< MAX_FABER_REQ_MP; ++k)
270 sint32 mpQuantity= 0;
271 root.getValueByName(mpQuantity, toString("faber.Create.Quantity formula %d", k+1).c_str() );
272 // if the req quantity is not 0
273 if ( mpQuantity>0 )
275 CFaberPlan::CFormulaMP mpVal;
276 mpVal.Quantity= mpQuantity;
277 // No error if unknown: filter not used (all MPs match)
278 TRANSLATE_VAL(val, toString("faber.Create.MP formula %d", k+1).c_str());
279 mpVal.ItemRequired.buildSheetId(val);
281 // Add this req MP.
282 FaberPlan.FormulaMps.push_back(mpVal);
284 // else subsequents MP slots are not used
285 else
286 break;
291 // read minmax range/cast time when guigui ready
292 TRANSLATE_VAL(MinCastTime, "Basics.MinCastTime");
293 TRANSLATE_VAL(MaxCastTime, "Basics.MaxCastTime");
294 TRANSLATE_VAL(MinRange, "Basics.MinRange");
295 TRANSLATE_VAL(MaxRange, "Basics.MaxRange");
297 // Read SPCost
298 TRANSLATE_VAL(SPCost, "Basics.SPCost");
300 // Read AvoidCyclic
301 TRANSLATE_VAL(AvoidCyclic, "Basics.AvoidCyclic");
303 // Read UsableWithEmptyHands
304 TRANSLATE_VAL(UsableWithEmptyHands, "Basics.UsableWithEmptyHands");
306 // Read Action Nature
307 string val;
308 TRANSLATE_ENUM(ActionNature, ACTNATURE::UNKNOWN, ACTNATURE::toActionNature, "Basics.Action Nature");
310 // Read CivRestriction
311 TRANSLATE_ENUM(CivRestriction, EGSPD::CPeople::Common, EGSPD::CPeople::fromString, "Basics.CivRestriction");
314 // **** parse properties, to precompute the Bricks Flags.
315 BrickRequiredFlags= 0;
316 for(i=0;i<Properties.size();i++)
318 string text= NLMISC::toLowerAscii(Properties[i].Text);
320 // If the property is an opening property
321 const string openingProp[]= { "opening_1:", "opening_2:", "opening_3:" };
322 // or if the property is a general brick flag
323 const string neededBrickFlag= "needed_brick_flag";
324 const uint nOpeningProp= sizeof(openingProp) / sizeof(openingProp[0]);
325 for(uint j=0;j<nOpeningProp;j++)
327 const string &prop= openingProp[j];
328 // if found this property
329 if( text.compare(0, prop.size(), prop)==0 ||
330 (j==0 && text.compare(0, neededBrickFlag.size(), neededBrickFlag)==0)
333 // get all the opening requirement
334 vector<string> strList;
335 strList.reserve(10);
336 splitString(text, ":", strList);
337 for(uint k=1;k<strList.size();k++)
339 // remove empty space, and convert to a bit
340 strRemoveChar(strList[k], ' ');
341 BRICK_FLAGS::TBrickFlag evFlag;
342 evFlag= BRICK_FLAGS::toBrickFlag(strList[k]);
343 if(evFlag!=BRICK_FLAGS::UnknownFlag)
344 BrickRequiredFlags|= (uint64(1)<<(uint)evFlag);
346 break;
351 // **** parse required skills
352 // parse the sheet str
353 string skillReqStr;
354 TRANSLATE_VAL(skillReqStr, "Basics.LearnRequiresOneOfSkills");
355 while(strFindReplace(skillReqStr, ":", " "));
356 listSkill.clear();
357 splitString(skillReqStr," ",listSkill);
358 // build the req skill array
359 RequiredOneOfSkills.clear();
360 RequiredOneOfSkills.reserve(listSkill.size()/2);
361 for(i=0;i<listSkill.size()/2;i++)
363 CSkillValue sv;
364 sv.Skill= SKILLS::toSkill(listSkill[i*2]);
365 fromString(listSkill[i*2+1], sv.Value);
366 // keep only whats work
367 if(sv.Skill!=SKILLS::unknown)
369 RequiredOneOfSkills.push_back(sv);
373 // parse the sheet str
374 skillReqStr.clear();
375 TRANSLATE_VAL(skillReqStr, "Basics.LearnRequiresSkills");
376 while(strFindReplace(skillReqStr, ":", " "));
377 listSkill.clear();
378 splitString(skillReqStr," ",listSkill);
379 // build the req skill array
380 RequiredSkills.clear();
381 RequiredSkills.reserve(listSkill.size()/2);
382 for(i=0;i<listSkill.size()/2;i++)
384 CSkillValue sv;
385 sv.Skill= SKILLS::toSkill(listSkill[i*2]);
386 fromString(listSkill[i*2+1], sv.Value);
387 // keep only whats work
388 if(sv.Skill!=SKILLS::unknown)
390 RequiredSkills.push_back(sv);
394 // **** parse required bricks
395 // parse the sheet str
396 string brickReqStr;
397 TRANSLATE_VAL(brickReqStr, "Basics.LearnRequiresBricks");
398 while(strFindReplace(brickReqStr, ":", " "));
399 std::vector<string> listBrick;
400 listBrick.clear();
401 splitString(brickReqStr," ",listBrick);
402 // build the req skill array
403 RequiredBricks.clear();
404 RequiredBricks.reserve(listBrick.size());
405 for(i=0;i<listBrick.size();i++)
407 CSheetId sheetId;
408 string str= toLowerAscii(listBrick[i]);
409 if(str.find(".sbrick")==string::npos)
410 str+= ".sbrick";
411 sheetId.buildSheetId(str);
412 if(sheetId!=CSheetId::Unknown)
414 RequiredBricks.push_back(sheetId);
418 // faction index
419 string faction;
420 root.getValueByName (faction, "Basics.Faction" );
421 FactionIndex = CStaticFames::getInstance().getFactionIndex( faction );
423 // min fame value
424 TRANSLATE_VAL(MinFameValue, "Basics.Minimum fame");
426 // **** Magic only: try to get a ResistType against this brick
427 for(i=0;i<Properties.size();i++)
429 string text= toLowerAscii(Properties[i].Text);
431 // *** If the property is a DamageType
432 const string dmgTypeProp= "ma_dmg_type:";
433 if( text.compare(0, dmgTypeProp.size(), dmgTypeProp)==0 )
435 // extract the dmg type
436 string dtStr= text.substr(dmgTypeProp.size());
437 strRemoveChar(dtStr, ' ');
438 DMGTYPE::EDamageType dt= DMGTYPE::stringToDamageType(dtStr);
439 if(dt!=DMGTYPE::UNDEFINED)
441 // Convert to a resist type
442 RESISTANCE_TYPE::TResistanceType rt= DMGTYPE::getAssociatedResistanceType(dt);
443 if(rt!=RESISTANCE_TYPE::None)
444 MagicResistType= rt;
448 // *** Do the same if the property is an effect family (affliction spells)
449 const string effectFamProp= "ma_effect:";
450 if( text.compare(0, effectFamProp.size(), effectFamProp)==0 )
452 // extract the effect family
453 string efStr= text.substr(effectFamProp.size());
454 strRemoveChar(efStr, ' ');
455 EFFECT_FAMILIES::TEffectFamily ef= EFFECT_FAMILIES::toEffectFamily(efStr);
456 if(ef!=EFFECT_FAMILIES::Unknown)
458 // Convert to a resist type
459 RESISTANCE_TYPE::TResistanceType rt= EFFECT_FAMILIES::getAssociatedResistanceType(ef);
460 if(rt!=RESISTANCE_TYPE::None)
461 MagicResistType= rt;
470 // ***************************************************************************
471 bool CSBrickSheet::isRoot() const
473 return BRICK_FAMILIES::isRootFamily(BrickFamily);
476 // ***************************************************************************
477 bool CSBrickSheet::isCredit() const
479 return BRICK_FAMILIES::isCreditFamily(BrickFamily);
482 // ***************************************************************************
483 bool CSBrickSheet::isMandatory() const
485 return BRICK_FAMILIES::isMandatoryFamily(BrickFamily);
488 // ***************************************************************************
489 bool CSBrickSheet::isOptional() const
491 return BRICK_FAMILIES::isOptionFamily(BrickFamily);
494 // ***************************************************************************
495 bool CSBrickSheet::isParameter() const
497 return BRICK_FAMILIES::isParameterFamily(BrickFamily);
500 // ***************************************************************************
501 bool CSBrickSheet::isCombat() const
503 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::COMBAT;
506 // ***************************************************************************
507 bool CSBrickSheet::isMagic() const
509 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::MAGIC;
512 // ***************************************************************************
513 bool CSBrickSheet::isFaber() const
515 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::FABER;
518 // ***************************************************************************
519 bool CSBrickSheet::isHarvest() const
521 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::HARVEST;
524 // ***************************************************************************
525 bool CSBrickSheet::isForageProspection() const
527 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::FORAGE_PROSPECTION;
530 // ***************************************************************************
531 bool CSBrickSheet::isForageExtraction() const
533 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::FORAGE_EXTRACTION;
536 // ***************************************************************************
537 bool CSBrickSheet::isSpecialPower() const
539 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::SPECIAL_POWER;
542 // ***************************************************************************
543 bool CSBrickSheet::isProcEnchantment() const
545 return BRICK_FAMILIES::brickType(BrickFamily) == BRICK_TYPE::PROC_ENCHANTEMENT;
548 // ***************************************************************************
549 bool CSBrickSheet::mustDisplayLevel() const
551 // always but if root or mandatory, special interface, or if level is 0
552 // NB: Yoyo Hack. special interface with indexInFamily==63 means "want to display the level"
553 return !( isMandatory() ||
554 isRoot() ||
555 //(BrickFamily>= BRICK_FAMILIES::BeginInterface && BrickFamily<= BRICK_FAMILIES::EndInterface && IndexInFamily!=63) ||
556 Level==0 );