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) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
31 using namespace NLMISC
;
32 using namespace NLGEORGES
;
35 // ***************************************************************************
36 // Easy Macro to translate .typ enum
37 #define TRANSLATE_ENUM( _Var_, _unknown_, _func_, _key_) \
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_) \
47 if( !root.getValueByName(val, _key_)) \
48 debug( toString("Key '%s' not found.", _key_) ); \
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
)
71 // read the array of skills
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
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
);
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" );
118 root
.getValueByName (Icon
, "Client.Icon" );
119 Icon
= toLowerAscii(Icon
);
120 IdIcon
= ClientSheetsStrings
.add(Icon
);
123 root
.getValueByName (IconBack
, "Client.IconBack" );
124 IconBack
= toLowerAscii(IconBack
);
125 IdIconBack
= ClientSheetsStrings
.add(IconBack
);
128 root
.getValueByName (IconOver
, "Client.IconOver" );
129 IconOver
= toLowerAscii(IconOver
);
130 IdIconOver
= ClientSheetsStrings
.add(IconOver
);
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();
147 for(i
=0;i
<SBRICK_MAX_MANDATORY
;i
++)
149 sprintf(tmp
, "Mandatory.f%d", i
);
150 root
.getValueByName (sTmp
, tmp
);
153 BRICK_FAMILIES::TBrickFamily bf
= BRICK_FAMILIES::toSBrickFamily(sTmp
);
154 if(bf
!= BRICK_FAMILIES::Unknown
)
155 MandatoryFamilies
.push_back( bf
);
157 nlwarning("Unknown Mandatory family %s",sTmp
.c_str());
162 OptionalFamilies
.clear();
163 for(i
=0;i
<SBRICK_MAX_OPTIONAL
;i
++)
165 sprintf(tmp
, "Optional.f%d", i
);
166 root
.getValueByName (sTmp
, tmp
);
169 BRICK_FAMILIES::TBrickFamily bf
= BRICK_FAMILIES::toSBrickFamily(sTmp
);
170 if(bf
!= BRICK_FAMILIES::Unknown
)
171 OptionalFamilies
.push_back( bf
);
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
);
185 BRICK_FAMILIES::TBrickFamily bf
= BRICK_FAMILIES::toSBrickFamily(sTmp
);
186 if(bf
!= BRICK_FAMILIES::Unknown
)
187 ParameterFamilies
.push_back( bf
);
189 nlwarning("Unknown Parameter family %s",sTmp
.c_str());
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
);
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
);
214 for(i
=0;i
<MaxProperties
;i
++)
217 root
.getValueByName(val
, toString("Basics.Property %d", i
).c_str() );
218 if(!val
.empty() && val
!="NULL")
222 Properties
.push_back(prop
);
227 // The FaberPlan are stored in Mandatory only, but the tool filter is in root
228 if( isFaber() && (isMandatory() || isRoot()) )
232 FaberPlan
.ItemPartMps
.clear();
233 FaberPlan
.FormulaMps
.clear();
236 TRANSLATE_VAL(val
, "faber.Create.Crafted Item");
237 FaberPlan
.ItemBuilt
.buildSheetId(val
);
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
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() );
260 FaberPlan
.ItemPartMps
.push_back(mpVal
);
262 // else subsequents MP slots are not used
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
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
);
282 FaberPlan
.FormulaMps
.push_back(mpVal
);
284 // else subsequents MP slots are not used
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");
298 TRANSLATE_VAL(SPCost
, "Basics.SPCost");
301 TRANSLATE_VAL(AvoidCyclic
, "Basics.AvoidCyclic");
303 // Read UsableWithEmptyHands
304 TRANSLATE_VAL(UsableWithEmptyHands
, "Basics.UsableWithEmptyHands");
306 // Read Action Nature
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
;
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
);
351 // **** parse required skills
352 // parse the sheet str
354 TRANSLATE_VAL(skillReqStr
, "Basics.LearnRequiresOneOfSkills");
355 while(strFindReplace(skillReqStr
, ":", " "));
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
++)
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
375 TRANSLATE_VAL(skillReqStr
, "Basics.LearnRequiresSkills");
376 while(strFindReplace(skillReqStr
, ":", " "));
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
++)
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
397 TRANSLATE_VAL(brickReqStr
, "Basics.LearnRequiresBricks");
398 while(strFindReplace(brickReqStr
, ":", " "));
399 std::vector
<string
> listBrick
;
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
++)
408 string str
= toLowerAscii(listBrick
[i
]);
409 if(str
.find(".sbrick")==string::npos
)
411 sheetId
.buildSheetId(str
);
412 if(sheetId
!=CSheetId::Unknown
)
414 RequiredBricks
.push_back(sheetId
);
420 root
.getValueByName (faction
, "Basics.Faction" );
421 FactionIndex
= CStaticFames::getInstance().getFactionIndex( faction
);
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
)
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
)
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() ||
555 //(BrickFamily>= BRICK_FAMILIES::BeginInterface && BrickFamily<= BRICK_FAMILIES::EndInterface && IndexInFamily!=63) ||