Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / ryzom / common / src / game_share / rm_family.cpp
blobcae1841170ddf8f1402c89d1e4d9f812f53231f0
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 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"
24 // nel
25 #include "nel/misc/string_conversion.h"
26 #include "nel/misc/i18n.h"
28 #include "rm_family.h"
30 using namespace std;
31 using namespace NLMISC;
33 namespace RM_FABER_TYPE
35 // The conversion table
36 const CStringConversion<TRMFType>::CPair stringTable [] =
38 { "MpL", MPL },
39 { "MpH", MPH },
40 { "MpP", MPP },
41 { "MpM", MPM },
42 { "MpG", MPG },
43 { "MpC", MPC },
44 { "MpGA", MPGA },
45 { "MpPE", MPPE },
46 { "MpCA", MPCA },
47 { "MpE", MPE },
48 { "MpEN", MPEN },
49 { "MpPR", MPPR },
50 { "MpCR", MPCR },
51 { "MpRI", MPRI },
52 { "MpRE", MPRE },
53 { "MpAT", MPAT },
54 { "MpSU", MPSU },
55 { "MpED", MPED },
56 { "MpBT", MPBT },
57 { "MpPES", MPPES },
58 { "MpSH", MPSH },
59 { "MpTK", MPTK },
60 { "MpJH", MPJH },
61 { "MpCF", MPCF },
62 { "MpVE", MPVE },
63 { "MpMF", MPMF }
66 std::string TypeToSheetEntry[]=
68 "A MpL (Blade)",
69 "B MpH (Hammer)",
70 "C MpP (Point)",
71 "D MpM (Shaft)",
72 "E MpG (Grip)",
73 "F MpC (Counterweight)",
74 "G MpGA (Trigger)",
75 "H MpPE (Firing pin)",
76 "I MpCA (Barrel)",
77 "J MpE (Explosive)",
78 "K MpEN (Ammo jacket)",
79 "L MpPR (Ammo bullet)",
80 "M MpCR (Armor shell)",
81 "N MpRI (Armor interior coating)",
82 "O MpRE (Armor interieur stuffing)",
83 "P MpAT (Armor clip)",
84 "Q MpSU (Jewel stone support)",
85 "R MpED (Jewel stone)",
86 "S MpBT (Blacksmith tool)",
87 "T MpPES (Pestle tool)",
88 "U MpSH (Sharpener tool)",
89 "V MpTK (Tunneling Knife)",
90 "W MpJH (Jewelry hammer)",
91 "X MpCF (Campfire)",
92 "Y MpVE (Clothes)",
93 "Z MpMF (Magic Focus)",
96 CStringConversion<TRMFType> conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
98 // convert type id to type name string
99 const std::string& toString( TRMFType faber_type )
101 return conversion.toString(faber_type);
104 // convert type name to type enum value
105 TRMFType toFaberType( const std::string& str )
107 return conversion.fromString(str);
110 const std::string &faberTypeToSheetEntry(TRMFType type)
112 nlctassert( (sizeof(TypeToSheetEntry)/sizeof(TypeToSheetEntry[0])) == NUM_FABER_TYPE );
114 if(type>=NUM_FABER_TYPE)
116 static std::string empty;
117 return empty;
120 return TypeToSheetEntry[type];
123 /// Client: use the CI18N
124 const std::string& toLocalString( TRMFType e )
126 return CI18N::get("mpft" + toString(e));
129 std::string toIconDefineString( TRMFType e )
131 return string("item_part_icon_") + RM_FABER_TYPE::toString(e);
137 namespace RM_FAMILY
139 /// Get the Localized UCString
140 const std::string& toLocalString( TRMFamily e )
142 return CI18N::get("mpfam" + toString(e));
147 namespace RM_GROUP
149 /// Get the Localized UCString
150 const std::string& toLocalString( TRMGroup e )
152 return CI18N::get("mpgroup" + toString(e));
157 namespace RM_FABER_PROPERTY
159 /// Get the Localized UCString
160 const std::string& toLocalString( TRMFProperty e )
162 return CI18N::get("mpprop" + toString(e));
167 // With which quality the RawMaterial can be used for craft
168 namespace RM_FABER_QUALITY
170 // The conversion table
171 const CStringConversion<TFaberQuality>::CPair stringTable [] =
173 { "Slightly", SLIGHTLY },
174 { "Moderately", MODERATELY },
175 { "Quite", QUITE },
176 { "Extremely", EXTREMELY }
179 CStringConversion<TFaberQuality> conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
181 // convert type id to type name string
182 const std::string& toString( TFaberQuality fq )
184 return conversion.toString(fq);
187 // convert type name to type enum value
188 TFaberQuality toFaberQuality( const std::string& str )
190 return conversion.fromString(str);
193 /// Client: use the CI18N
194 const std::string& toLocalString( TFaberQuality e )
196 return CI18N::get("mpfq" + toString(e));
202 namespace RM_COLOR
205 const std::string ColorTable[]=
207 "Red",
208 "Beige",
209 "Green",
210 "Turquoise",
211 "Blue",
212 "Purple",
213 "White",
214 "Black",
217 const std::string UnknownColor= "Unknown";
219 const std::string& toString( sint value )
221 nlctassert(sizeof(ColorTable)/sizeof(ColorTable[0]) == NumColors);
223 if( validColor(value) )
224 return ColorTable[value];
225 else
226 return UnknownColor;
229 /// Get the Localized UCString
230 const std::string& toLocalString( sint value )
232 return CI18N::get("mpcol" + toString(value));
235 };//RM_COLOR
238 namespace RM_FABER_STAT_TYPE
241 // The conversion table
242 const CStringConversion<TRMStatType>::CPair stringTable [] =
244 { "Durability", Durability },
245 { "Weight", Weight },
246 { "SapLoad", SapLoad },
247 { "DMG", DMG },
248 { "Speed", Speed },
249 { "Range", Range },
250 { "DodgeModifier", DodgeModifier },
251 { "ParryModifier", ParryModifier },
252 { "AdversaryDodgeModifier", AdversaryDodgeModifier },
253 { "AdversaryParryModifier", AdversaryParryModifier },
254 { "ProtectionFactor", ProtectionFactor },
255 { "MaxSlashingProtection", MaxSlashingProtection },
256 { "MaxBluntProtection", MaxBluntProtection },
257 { "MaxPiercingProtection", MaxPiercingProtection },
258 { "AcidProtection", AcidProtection },
259 { "ColdProtection", ColdProtection },
260 { "FireProtection", FireProtection },
261 { "RotProtection", RotProtection },
262 { "ShockWaveProtection", ShockWaveProtection },
263 { "PoisonProtection", PoisonProtection },
264 { "ElectricityProtection", ElectricityProtection },
265 { "DesertResistance", DesertResistance },
266 { "ForestResistance", ForestResistance },
267 { "LacustreResistance", LacustreResistance },
268 { "JungleResistance", JungleResistance },
269 { "PrimaryRootResistance", PrimaryRootResistance },
270 { "ElementalCastingTimeFactor", ElementalCastingTimeFactor },
271 { "ElementalPowerFactor", ElementalPowerFactor },
272 { "OffensiveAfflictionCastingTimeFactor", OffensiveAfflictionCastingTimeFactor },
273 { "OffensiveAfflictionPowerFactor", OffensiveAfflictionPowerFactor },
274 { "DefensiveAfflictionCastingTimeFactor", DefensiveAfflictionCastingTimeFactor },
275 { "DefensiveAfflictionPowerFactor", DefensiveAfflictionPowerFactor },
276 { "HealCastingTimeFactor", HealCastingTimeFactor },
277 { "HealPowerFactor", HealPowerFactor },
280 CStringConversion<TRMStatType> conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
282 const std::string& toString( TRMStatType stats )
284 // must change the convsersion table
285 nlctassert(NumRMStatType == sizeof(stringTable)/sizeof(stringTable[0]));
286 return conversion.toString(stats);
289 const std::string& toLocalString( TRMStatType stats )
291 // must change en.uxt
292 nlctassert(NumRMStatType == sizeof(stringTable)/sizeof(stringTable[0]));
293 return CI18N::get("mpstat" + NLMISC::toString((uint)stats));
296 // Array saying for which item part built, what stat is useful
297 class CItemPartToStat
299 public:
300 bool StatRelevant[RM_FABER_TYPE::NUM_FABER_TYPE * NumRMStatType];
302 CItemPartToStat()
304 memset(StatRelevant, 0, RM_FABER_TYPE::NUM_FABER_TYPE*NumRMStatType*sizeof(bool));
307 void setStatLine(RM_FABER_TYPE::TRMFType ft, const uint8 vals[NumRMStatType])
309 nlassert(sizeof(uint8)==sizeof(bool));
310 nlassert(ft<RM_FABER_TYPE::NUM_FABER_TYPE);
311 memcpy(StatRelevant + ft*NumRMStatType, vals, NumRMStatType*sizeof(bool));
315 static CItemPartToStat ItemPartToStat;
318 bool isStatRelevant(RM_FABER_TYPE::TRMFType ft, TRMStatType fs)
320 // must change the setup below
321 nlctassert(NumRMStatType == 34 && RM_FABER_TYPE::NUM_FABER_TYPE == 26);
322 if(ft>=RM_FABER_TYPE::NUM_FABER_TYPE || fs>=NumRMStatType)
323 return false;
325 // build
326 static bool init= false;
327 if(!init)
329 init= true;
330 // Hardcoded array
331 // Dur Wgt Sap Dmg Spd Rng Dog Par ADo APa Prt Slh Bln Prc Apt CPt FPt RPt SPt PPt EPt DRt FRt LRt JRt PRt DAC DAP DHC DHP OAC OAP OEC OEP
332 const uint8 mpL[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Blade
333 const uint8 mpH[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Hammer
334 const uint8 mpP[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Point
335 const uint8 mpM[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Shaft
336 const uint8 mpG[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Grip
337 const uint8 mpC[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Counterweight
338 const uint8 mpGA[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Trigger
339 const uint8 mpPE[]= {1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Firing pin
340 const uint8 mpCA[]= {1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Barrel
341 const uint8 mpE[]= {1 ,1 ,0 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Explosive
342 const uint8 mpEN[]= {1 ,1 ,0 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Ammo jacket
343 const uint8 mpPR[]= {1 ,1 ,0 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Ammo bullet
344 const uint8 mpCR[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Armor shell
345 const uint8 mpRI[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Lining
346 const uint8 mpRE[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Stuffing
347 const uint8 mpAT[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Armor clip
348 const uint8 mpSU[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewel stone support
349 const uint8 mpED[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewel stone
350 const uint8 mpBT[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Blacksmith tool
351 const uint8 mpPES[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Pestle tool
352 const uint8 mpSH[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Sharpener tool
353 const uint8 mpTK[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // TunnelingKnife
354 const uint8 mpJH[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewelry hammer
355 const uint8 mpCF[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Campfire
356 const uint8 mpVE[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Clothes
357 const uint8 mpMF[]= {1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 }; // Magic Focus
358 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPL, mpL);
359 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPH, mpH);
360 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPP, mpP);
361 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPM, mpM);
362 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPG, mpG);
363 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPC, mpC);
364 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPGA, mpGA);
365 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPE, mpPE);
366 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCA, mpCA);
367 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPE, mpE);
368 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPEN, mpEN);
369 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPR, mpPR);
370 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCR, mpCR);
371 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPRI, mpRI);
372 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPRE, mpRE);
373 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPAT, mpAT);
374 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPSU, mpSU);
375 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPED, mpED);
376 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPBT, mpBT);
377 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPES, mpPES);
378 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPSH, mpSH);
379 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPTK, mpTK);
380 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPJH, mpJH);
381 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCF, mpCF);
382 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPVE, mpVE);
383 ItemPartToStat.setStatLine(RM_FABER_TYPE::MPMF, mpMF);
386 // get
387 return ItemPartToStat.StatRelevant[ft*NumRMStatType + fs];
391 // ***************************************************************************
392 // GamePlay tuning
393 const float StretchStatMinDeltaWanted= 0.3f;
394 const float StretchStatMaxDeltaFactor= 2.0f;
395 const float StretchStatBonusDeltaThreshold= 0.35f; // if the player get +35% or more in one stat
396 const float StretchStatBonusValue= 0.1f; // then +10%!
397 // stretch the item stats
398 void stretchItemStats(float array[NumRMStatType], uint64 statBitField, bool addBonusRule)
400 uint i;
402 // *** ensure 0-1.
403 for(i=0;i<NumRMStatType;i++)
405 clamp(array[i], 0.f, 1.f);
408 // *** compute the mean of each stat used, the max stat value
409 float energy= 0;
410 uint numStatUsed= 0;
411 float maxStat= 0;
412 uint bestStat= 0;
413 for(i=0;i<NumRMStatType;i++)
415 // if the item use this stat
416 if(statBitField&((uint64)(1)<<i))
418 energy+= array[i];
419 numStatUsed++;
420 if(array[i]>maxStat)
422 maxStat= array[i];
423 bestStat= i;
427 // mean, and difference max-mean
428 float meanStat= 0;
429 if(numStatUsed)
430 meanStat= energy / numStatUsed;
431 float maxDelta= maxStat-meanStat;
432 // if all stats are equals, no-op
433 if(maxDelta==0.f)
434 return;
436 // *** if the player has succed in applying a nearly perfect bonus for its best stat (eg:+40%), add an additional bonus
437 float bestStatBonusValue= 0.f;
438 // also, add this bonus only if addBonusRule
439 if(addBonusRule && maxDelta>=StretchStatBonusDeltaThreshold)
441 bestStatBonusValue= StretchStatBonusValue;
444 // *** stretch the maxDelta so it reaches at least StretchStatMinDeltaWanted (eg: +30%)
445 if(maxDelta < StretchStatMinDeltaWanted)
447 float stretchFactor= StretchStatMinDeltaWanted / maxDelta;
448 stretchFactor= min(stretchFactor, StretchStatMaxDeltaFactor);
450 // For all stats, stretch the delta
451 float newEnergy=0;
452 float numStatNot0= 0;
453 float numStatNot1= 0;
454 for(i=0;i<NumRMStatType;i++)
456 // if the item use this stat
457 if(statBitField&((uint64)(1)<<i))
459 float delta= array[i] - meanStat;
460 delta*= stretchFactor;
461 // add the delta to the mean
462 array[i]= meanStat+delta;
463 clamp(array[i], 0.f, 1.f);
465 /// new sum of stats
466 newEnergy+= array[i];
468 // count not clamped stats
469 if(array[i]>0.f)
470 numStatNot0++;
471 if(array[i]<1.f)
472 numStatNot1++;
476 /// loss or gain of energy because of Clamp 0 or 1 ?
477 float deltaEnergy = newEnergy - energy;
479 // while more than 0.1% of energy loss or gained (float precision...)
480 uint nbPass=0;
481 while(fabs(deltaEnergy)>(0.001f*numStatUsed))
483 // redistribute delta among all stats not clamped
484 float deltaStat;
485 // if we gain too much energy because of ClampTo0, decrement Not0 Stats.
486 if(deltaEnergy>0)
487 deltaStat= -deltaEnergy/numStatNot0;
488 // if we loss too much energy because of ClampTo1, increment Not1 Stats.
489 else
490 deltaStat= -deltaEnergy/numStatNot1;
492 // redistribute all stats, to get correct energy
493 newEnergy=0;
494 numStatNot0= 0;
495 numStatNot1= 0;
496 for(i=0;i<NumRMStatType;i++)
498 // if the item use this stat
499 if(statBitField&((uint64)(1)<<i))
501 // add delta, and re-clamp
502 array[i]+= deltaStat;
503 clamp(array[i], 0.f, 1.f);
504 newEnergy+= array[i];
506 if(array[i]>0.f)
507 numStatNot0++;
508 if(array[i]<1.f)
509 numStatNot1++;
514 // This pass may have regenerate clamp problems.
515 deltaEnergy = newEnergy - energy;
517 // this cannot happen, because in the worst case, all stats are clamped to 0 or 1.
518 nbPass++;
519 nlassert(nbPass<=NumRMStatType);
524 // **** add the best stat bonus value (at the end)
525 if(bestStatBonusValue)
527 array[bestStat]+= bestStatBonusValue;
528 clamp(array[bestStat], 0.f, 1.f);
533 // ***************************************************************************
534 // method used by getStatFinalValidity, for both magic protection and magic resist
535 uint64 filterStatValidity3BestOne(const float array[NumRMStatType], uint64 bfIn, uint statStart, uint statEnd)
537 uint64 ret= bfIn;
539 // init 3 bests
540 const uint NumBests= 3;
541 sint bestStat[NumBests];
542 float bestValues[NumBests];
543 for(uint i=0;i<NumBests;i++)
545 bestStat[i]= -1;
546 bestValues[i]= -1;
549 // get 3 bests
550 for(uint i=statStart;i<statEnd;i++)
552 // if the stat is really present
553 if(bfIn & uint64(1)<<i)
555 // get the stat value
556 float val= array[i];
557 if( val > bestValues[0] )
559 bestStat[2]= bestStat[1];
560 bestValues[2]= bestValues[1];
561 bestStat[1]= bestStat[0];
562 bestValues[1]= bestValues[0];
563 bestStat[0]= i;
564 bestValues[0]= val;
566 else if( val > bestValues[1] )
568 bestStat[2]= bestStat[1];
569 bestValues[2]= bestValues[1];
570 bestStat[1]= i;
571 bestValues[1]= val;
573 else if( val > bestValues[2] )
575 bestStat[2]= i;
576 bestValues[2]= val;
581 // then keep only bits that are sets
582 for(uint i=statStart;i<statEnd;i++)
584 // if the stat is really present
585 if(bfIn & uint64(1)<<i)
587 // test if 1 of 3 best stat match the index
588 bool keep= false;
589 for(uint bt=0;bt<NumBests;bt++)
591 if(bestStat[bt]==(sint)i)
593 keep= true;
594 break;
597 // don't keep? reset bit
598 if(!keep)
600 ret&= ~(uint64(1)<<i);
605 return ret;
608 // ***************************************************************************
609 uint64 getStatFinalValidity(const float array[NumRMStatType], uint64 statBitField)
611 uint64 ret= statBitField;
613 // *** Magic Protections
614 // should have 7 magic protection. Acid should be the 1st, and Eletrcity the last
615 const uint startMProt= AcidProtection;
616 const uint endMProt= ElectricityProtection+1;
617 nlctassert(endMProt - startMProt == 7);
619 // if the item has some magic protection
620 const uint64 mProtBF= ((uint64(1) << endMProt)-1) - ((uint64(1) << startMProt)-1);
621 if(statBitField & mProtBF)
623 ret= filterStatValidity3BestOne(array, ret, startMProt, endMProt);
626 // *** Magic Resistances
627 // should have 5 magic resistances. Desert should be the 1st, and PrimRoot the last
628 const uint startMResist= DesertResistance;
629 const uint endMResist= PrimaryRootResistance+1;
630 nlctassert(endMResist - startMResist == 5);
632 // if the item has some magic Resistection
633 const uint64 mResistBF= ((uint64(1) << endMResist)-1) - ((uint64(1) << startMResist)-1);
634 if(statBitField & mResistBF)
636 ret= filterStatValidity3BestOne(array, ret, startMResist, endMResist);
639 return ret;
642 // ***************************************************************************
643 bool isMagicResistStat(TRMStatType fs)
645 // should have 5 magic resistances. Desert should be the 1st, and PrimRoot the last
646 const sint startMResist= DesertResistance;
647 const sint endMResist= PrimaryRootResistance+1;
648 nlctassert(endMResist - startMResist == 5);
650 return fs>=startMResist && fs<endMResist;
653 // ***************************************************************************
654 bool isMagicProtectStat(TRMStatType fs)
656 // should have 7 magic protection. Acid should be the 1st, and Electricity the last
657 const sint startMProt= AcidProtection;
658 const sint endMProt= ElectricityProtection+1;
659 nlctassert(endMProt - startMProt == 7);
661 return fs>=startMProt && fs<endMProt;
666 namespace RM_CLASS_TYPE
669 const std::string &toLocalString(TRMClassType classType)
671 return CI18N::get(toString("uiItemRMClass%d", classType));