1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #ifndef NL_SRG_UTILITIES_H
20 #define NL_SRG_UTILITIES_H
24 #include <nel/misc/types_nl.h>
25 #include <nel/misc/sstring.h>
26 #include "nel/misc/path.h"
27 #include "nel/misc/file.h"
28 #include "nel/misc/smart_ptr.h"
29 #include "nel/misc/command.h"
30 #include "nel/misc/common.h"
31 #include "nel/misc/path.h"
32 #include <nel/misc/diff_tool.h>
33 #include <nel/misc/random.h>
35 #include "nel/georges/u_form.h"
36 #include "nel/georges/u_form_elm.h"
37 #include "nel/georges/u_form_dfn.h"
38 #include "nel/georges/u_form_loader.h"
39 #include "nel/georges/u_type.h"
40 // Georges, bypassing interface
41 #include "georges/stdgeorges.h"
42 #include "georges/form.h"
51 using namespace NLMISC
;
52 using namespace NLGEORGES
;
56 typedef sint TFaberInterestLevel
;
57 const TFaberInterestLevel NAInterestLevel
= -1;
58 const uint32 NbNomenclaturedFaberLevel
= 5; // excluding N/A
59 const char *sNomenclaturedInterestLevels
[NbNomenclaturedFaberLevel
+1] = { "N/A", "Worst", "Bad", "Average", "Good", "Best" }; // from -1 to 5
60 CRandom RandomGenerator
;
62 typedef CVectorSString vs
;
64 typedef map
<uint32
, vector
<uint32
> > CRulesFilter
;
65 typedef map
<CSString
, vector
<uint32
>, CUnsensitiveSStringLessPred
> CRulesStrFilter
;
66 typedef map
<CSString
, vs
, CUnsensitiveSStringLessPred
> CRulesStr2Filter
;
67 typedef map
< CSString
, CSString
, CUnsensitiveSStringLessPred
> mss
;
68 typedef vector
<uint32
> vu
;
69 typedef hash_map
< string
, string
, hash
<string
> > CTitles
; // code -> utf8 title
71 // Write sheet files, or display to screen only
72 bool WriteSheetsToDisk
= true;
74 // Overriden by command-line argument after -n
75 string ExtractNamesCsv
;
78 const uint32 NbFaberElements
= 26;
80 const uint32 RM_INDEX_FAMILY_CODE
= 1;
81 const uint32 NB_FAMILY_CODE_CHARS
= 4;
83 const uint32 RM_INDEX_CREATURE_CODE
= 5;
84 const uint32 NB_CREATURE_CODE_CHARS
= 5;
86 const uint32 RM_INDEX_ECOSYSTEM_CODE
= 8;
87 const uint32 NB_ECOSYSTEM_CODE_CHARS
= 1;
89 const uint32 RM_INDEX_LEVELZONE_CODE
= 9;
91 const uint32 SIZE_RAW_MATERIAL_SHEET_FILENAME
= 18;
93 /*const uint32 RM_INDEX_INTEREST_LEVEL = 6;
94 const uint32 NB_INTEREST_LEVEL_CHARS = 2;
96 const uint32 RM_INDEX_FABERELEMS_CODE = 8;
97 const uint32 NB_FABERELEMS_CODE_CHARS = 6;*/
99 const uint32 CR_INDEX_ECOSYSTEM_CODE
= 3; // in creature code
100 const uint32 CR_INDEX_LEVELZONE_CODE
= 4;
101 const uint32 CR_INDEX_LOCAL_LEVEL_CODE
= 5;
103 //const uint32 NB_UNIQUE_LEVELS_PER_ZONE = 4; // not counting the bosses, the fifth equals the first one of next
104 const uint32 NB_UNIQUE_LEVELZONES_PER_CONTINENT
= 5;
105 const uint32 MAX_NB_LOCAL_LEVELS
= 8;
106 //const uint32 NB_ZONE_LEVELS = 5; // TEMP for forest ecosystem
107 //const uint32 NbFaberInterestLevels = (NB_ZONE_LEVELS * NB_UNIQUE_LEVELS_PER_ZONE) + 1; // excluding N/A
109 string inputSheetPath
;
110 bool inputSheetPathLoaded
= false;
111 map
<string
, string
> inputSheetPathContent
; // short filename without ext, full filename with path
112 string TranslationPath
;
116 // These vectors have the same indices : by family
118 vs familyCodes
, ecosystemCodes
, propertyCodes
, creatureCodes
;
120 sint MaxFamilyNum
= -1;
122 enum TColor
{ Red
, Beige
, Green
, Turquoise
, Blue
, Violet
, White
, Black
, NbColors
, InvalidColor
=NbColors
};
124 // By ecosystem, color, property, rm group, creature, season
125 vs ecosystems
, colors
, properties
, groups
, creatures
, seasons
;
128 // DtName must be the 1st one
129 enum TDataCol
{ DtName
, DtTitle
, DtRMFamily
, DtGroup
, DtEcosystem
, DtLevelZone
, DtStatQuality
, DtProp
, DtCreature
, DtCreaTitle
, DtCraftSlotName
, DtCraftCivSpec
, DtColor
, DtAverageEnergy
, DtMaxLevel
, DtJewelProtectionType
, DtCustomizedProperties
, DtNbCols
};
130 const char *DataColStr
[DtNbCols
] = { "Code", "Name", "Family", "Group", "Ecosystem", "LevelZone", "Stat Quality", "Properties", "Creature sheets", "Creatures", "Item parts", "Craft civ spec", "Color", "Average energy", "Max level", "Jewel protection type", "Customized" };
132 const uint32 NbPropertyDepths
= 5;
133 const char *PropertyDepths
[NbPropertyDepths
] = { "Unknown", "Slightly", "Moderately", "Quite", "Extremely" };
135 typedef uint32 TGroup
; // index in groups, and value in groups .typ
137 //enum TLocation { Under, Over, Flora, Creatures, NB_LOCATIONS, NB_DEPOSITS=Flora+1 };
138 enum TLocation
{ InDeposits
, InCreatures
, NB_LOCATIONS
};
139 vu DepositFamilyIndices
;
141 enum TCiv
{ Fyros
, Matis
, Tryker
, Zorai
, AllCiv
, NbCiv
};
142 const char *CivNames
[NbCiv
] = { "Fyros", "Matis", "Tryker", "Zorai", "All" };
143 const char *CivEcosystemCodes
[NbCiv
] = { "D", "F", "L", "J", "X" };
145 enum TEcosystem
{ CommonEcosystem
, Desert
, Forest
, Lacustre
, Jungle
, PrimeRoots
, NbEcosystems
, Goo
=NbEcosystems
, Invasion
, Raid
, Event
, N
, S
, T
, U
, V
, W
, X
, Y
, Z
, NbEcosystemsPlusExtensions
};
147 TCiv EcosystemToCiv
[NbEcosystems
] = { AllCiv
, Fyros
, Matis
, Tryker
, Zorai
, AllCiv
};
149 enum TStatQuality
{ Basic
, Fine
, Choice
, Excellent
, Supreme
, NbStatQualities
, InvalidStatQuality
=NbStatQualities
};
150 char *StatQualityStr
[NbStatQualities
+1] = { "Basic", "Fine", "Choice", "Excellent", "Supreme", "N/A" };
152 TStatQuality CreatureLocalLevelToStatQuality
[MAX_NB_LOCAL_LEVELS
] =
154 Basic
, // 1 (index 0)
158 Excellent
, // 5 (named creatures)
159 InvalidStatQuality
, // 6 (mission creatures, their RMs have no craft stats)
160 Supreme
, // 7 (bosses)
161 Choice
// 8 (mini-bosses)
165 enum TIndexOfRemarkableStatIndex
{ RBestA
, RBest
=RBestA
, RWorstA1
, RWorst1
=RWorstA1
, RWorstA2
, RWorst2
=RWorstA2
, RBestB
, RWorstB1
, RWorstB2
, NB_REMARKABLE_STAT_INDICES
};
169 TSkeletonInfo() : IsUsed(false) {}
175 bool IsUsed
; // true if has RM
178 typedef map
<CSString
, TSkeletonInfo
, CUnsensitiveSStringLessPred
> CSkeletonMap
;
180 // By skeleton group, by skeleton, bu creature model
181 CRulesStr2Filter SkgroupToModels
;
182 CSkeletonMap CreatureModels
;
184 //vs skeletonGroupColumns;
185 set
<CSString
, CUnsensitiveSStringLessPred
> CreatureMainModelsWithoutRM
;
188 CRulesStrFilter RMFamilyIndicesByCreatureModel
;
189 vu GooCreatureFamilyIndices
;
191 typedef map
<char, vu
> CCreatureTypeToFamilyIndices
; // the key is the 2nd char of the creature code (h for herbivore, k for kitin...)
192 CCreatureTypeToFamilyIndices InvasionRaidCreatureFamilyIndices
;
196 TBool() : Done(false) {}
200 typedef map
< uint
, TBool
> CDoneMap
; // indexed by levelzone
201 map
< string
, TBool
> IsRMSheetGenerated
; // indexed by sheetname
203 string rawMaterialPath
, creaturePath
, creatureAssignmentPath
, depositPath
;
207 const string oldRmSheetType
= "item";
208 const string rmSheetType
= "sitem";
209 const string crSheetType
= "creature";
210 const string dpSheetType
= "deposit";
213 //const uint32 NbPropertySlots = 10; // obsolete
214 uint32 UndefinedProperty
= ~0;
223 CSString IconBackground
, Icon
, IconOver
, IconOver2
;
226 map
< CSString
, CIconInfo
, CUnsensitiveSStringLessPred
> Icons
;
237 CMainStat() : SumNbFaberElemsFilled(0), MaxNbFaberElemsFilled(0), MinNbFaberElemsFilled(~0),
238 NbRMByFaberElem( NbFaberElements
, 0 ) {}
240 /// Call it when families etc. are ready
243 NbRMByFaberElemByFamilyAndCiv
.resize( families
.size() );
244 for ( uint32 i
=0; i
!=families
.size(); ++i
)
246 NbRMByFaberElemByFamilyAndCiv
[i
].resize( NbCiv
);
247 for ( uint32 c
=0; c
!=NbCiv
; ++c
)
248 NbRMByFaberElemByFamilyAndCiv
[i
][c
].resize( NbFaberElements
);
251 for ( uint32 iEcosystem
=0; iEcosystem
!=ecosystems
.size(); ++iEcosystem
)
253 for ( uint32 iLoc
=0; iLoc
!=NB_LOCATIONS
; ++iLoc
)
255 NbRMHavingProperty
[iLoc
][iEcosystem
].resize( properties
.size() );
256 NbRMByFaberElemByEcosystem
[iEcosystem
].resize( NbFaberElements
);
262 bool updateCraftStatistics( uint32 rFaberElem
, uint32 iEcosystem
, uint32 iFam
, TCiv civ
);
265 void updateMainStats( uint32 nbFaberElemsFilled
)
267 SumNbFaberElemsFilled
+= nbFaberElemsFilled
;
268 if ( nbFaberElemsFilled
< MinNbFaberElemsFilled
)
269 MinNbFaberElemsFilled
= nbFaberElemsFilled
;
270 if ( nbFaberElemsFilled
> MaxNbFaberElemsFilled
)
271 MaxNbFaberElemsFilled
= nbFaberElemsFilled
;
274 // By property and by ecosystem and location
275 vu NbRMHavingProperty
[NB_LOCATIONS
][NbEcosystems
];
277 uint32 SumNbFaberElemsFilled
;
278 uint32 MaxNbFaberElemsFilled
;
279 uint32 MinNbFaberElemsFilled
;
281 vector
<uint32
> NbRMByFaberElem
;;
282 vector
<uint32
> NbRMByFaberElemByEcosystem
[NbEcosystems
];
283 vector
< vector
< vector
< uint32
> > > NbRMByFaberElemByFamilyAndCiv
;
293 if (inputSheetPathLoaded
)
296 NLMISC::createDebug();
297 NLMISC::WarningLog
->addNegativeFilter( "CPath::insertFileInMap" );
299 CPath::addSearchPath(inputSheetPath
, true, false); // for Georges to work properly
301 vector
<string
> files
;
302 CPath::getPathContent (inputSheetPath
, true, false, true, files
);
305 for (i
=0; i
<files
.size(); ++i
)
307 string filename
= files
[i
];
308 string filebase
= CFile::getFilenameWithoutExtension(filename
);
309 inputSheetPathContent
[filebase
] = filename
;
312 inputSheetPathLoaded
= true;
317 * Get a random value in range [0..nbPossibleValues[
318 * Precondition: nbPossibleValues < 65536
320 inline uint32
getRandomValue( uint32 nbPossibleValues
)
322 /*double r = (double) rand();
323 r/= (double) (RAND_MAX+1); // exclude the ceiling value
324 return (uint32)(r * nbPossibleValues);*/
325 return RandomGenerator
.rand( (uint16
)(nbPossibleValues
-1) );
335 CDfnFieldInfo( const vector
<string
>& values
, const vector
<string
>& labels
) : TypePredefinedValues(values
), TypePredefinedLabels(labels
) {}
337 vector
<string
> TypePredefinedValues
;
338 vector
<string
> TypePredefinedLabels
;
347 const char *FamilyName
;
348 const char *IconFilename
;
355 sint
getNomenclaturedInterestLevel( TFaberInterestLevel level
, TFaberInterestLevel nbInterestLevels
)
357 return (level
== NAInterestLevel
) ? 0 : (level
* NbNomenclaturedFaberLevel
/ nbInterestLevels
) + 1;
366 // char Ch[NB_FABERELEMS_CODE_CHARS];
373 //struct CFaberCombination
375 // CFaberCombination( TFaberInterestLevel firstLevel, const string& code ) : FirstLevel(firstLevel)
377 // memcpy( Code.Ch, &code[0], NB_FABERELEMS_CODE_CHARS );
380 // TFaberInterestLevel FirstLevel;
388 //class CSheetNameRepository
393 // void resize( uint32 nbEcosystems, uint32 nbFamilies )
395 // _Container.resize( nbEcosystems );
396 // for ( uint32 i=0; i!=nbEcosystems; ++i )
398 // _Container[i].resize( nbFamilies );
403 // void insert( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, TFaberInterestLevel level, const string& faberCombinationCode )
405 // // nlassert( faberCombinationCode.size() == NB_FABERELEMS_CODE_CHARS );
406 // _Container[iEcosystem][iFamily][iCreature].push_back( CFaberCombination( level, faberCombinationCode ) );
410 // void getFaberCombinationCodes( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, vector<CFaberCombination> **codes )
412 // map < uint32, vector< CFaberCombination > >::iterator im;
413 // /*nldebug( "%u %u -> %u cr (searching for %u)", iEcosystem, iFamily, _Container[iEcosystem][iFamily].size(), iCreature );
414 // for ( im=_Container[iEcosystem][iFamily].begin(); im!=_Container[iEcosystem][iFamily].end(); ++im )
415 // nldebug( "cr %u -- %u combinations", (*im).first, (*im).second.size() );*/
416 // im = _Container[iEcosystem][iFamily].find( iCreature );
417 // if ( im == _Container[iEcosystem][iFamily].end() )
420 // *codes = &((im)->second);
425 // /// Indexs: iEcosystem, iFamily, rCreatureSpecialization, rFaberCombination
426 // vector< vector < map < uint32, vector< CFaberCombination > > > > _Container;
430 //void CSheetNameRepository::getFaberCombinationCodes( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, vector<CFaberCombination> **codes )
433 //CSheetNameRepository RawMaterialRepository;
437 * When adding a new characteristic, ADD IT INTO "v3_source_tables.xls!Item Parts v3"/"rm_item_parts.csv" and mark compatible item parts
439 enum TFaberCharacteristic
{
440 Durability
, Weight
, SapLoad
, DMG
, Speed
, Range
,
441 DodgeModifier
, ParryModifier
, AdversaryDodgeModifier
, AdversaryParryModifier
,
442 ProtectionFactor
, MaxSlashProtect
, MaxBluntProtect
, MaxPierceProtect
,
451 const char *sCharacs
[NbCharacs
] = {
452 "Durability", "Weight", "SapLoad", "DMG", "Speed", "Range",
453 "DodgeModifier", "ParryModifier", "AdversaryDodgeModifier", "AdversaryParryModifier",
454 "ProtectionFactor", "MaxSlashingProtection", "MaxBluntProtection", "MaxPiercingProtection",
455 "ElementalCastingTimeFactor", "ElementalPowerFactor",
456 "OffensiveAfflictionCastingTimeFactor", "OffensiveAfflictionPowerFactor",
457 "HealCastingTimeFactor", "HealPowerFactor",
458 "DefensiveAfflictionCastingTimeFactor", "DefensiveAfflictionPowerFactor",
459 "JewelProtectionType",
462 //const bool PositiveCharacs [NbCharacs] = { true, false, true, false, true, true };
463 //const float MinCharacValues [NbCharacs] = { 100, 0.1f, 10, 0.2f, 100, 0 };
464 //const float MaxCharacValues [NbCharacs] = { 500, 1.5f, 400, 2.0f, 500, 60 };
465 //const float PeakCharacValues [NbCharacs] = { 2000, 2.5f, 800, 5.0f, 2000, 80 };
466 bool PositiveCharacs
[NbCharacs
];
467 float MinCharacValues
[NbCharacs
];
468 float MaxCharacValues
[NbCharacs
];
469 float PeakCharacValues
[NbCharacs
];
470 vector
<bool> CharacSlotFilter
[NbCharacs
]; // it's a positive filter
479 vs Properties
; // ex: propForA, propForB, propForC
480 CSString CompatibleCraftParts
; // ex: ABC
481 vu CraftPartsByProp
; // ex: 0, 1, 2 (indices in ompatibleCraftParts)
482 vector
<TCiv
> Civs
; // ex: Fyros, All, All
483 vu Freqs
; // ex: 1, 2, 2
485 bool IsActive
; // False if not in rm_fam_prop.csv
488 CSString SpecialCreatureTag
;
491 sint8 RemarkableStatIndex
[NB_REMARKABLE_STAT_INDICES
];
493 sint8 JewelProtIndex
;
495 static uint UseGenerateOnly
; // if 0, generate all; otherwise, generate only families that have GenerateOnly set to true
498 TFamInfo() : Group(~0), IsInDeposits(false), IsActive(false), IsInCreatures(false), SpecialCreatureTag(), GenerateOnly(false), IsForMission(false), ColorIndex(-1), JewelProtIndex(-1) {}
501 uint
getCompatibleCraftPart( uint iCompatibleCP
) const
503 return (uint
)(CompatibleCraftParts
[iCompatibleCP
] - 'A');
506 /// whichProp: index in Properties
507 uint
getBeginCraftPartForProp( uint whichProp
) const
509 return CraftPartsByProp
[whichProp
];
512 /// whichProp: index in Properties
513 uint
getEndCraftPartForProp( uint whichProp
) const
515 if ( whichProp
== Properties
.size()-1 )
516 return CompatibleCraftParts
.size();
518 return CraftPartsByProp
[whichProp
+1];
521 /// whichProp: index in Properties
522 CSString
getCraftPartForProp( uint whichProp
) const
524 uint start
= getBeginCraftPartForProp( whichProp
);
525 return CompatibleCraftParts
.substr( start
, getEndCraftPartForProp( whichProp
) - start
);
528 /** With the returned index, you can get elt in Property, CraftPartsByProp, Civs and Freqs.
529 * Returns ~0 if not found.
531 uint
getPropIndexByCraftPart( char itemPart
) const
533 for ( uint i
=0; i
!=CraftPartsByProp
.size(); ++i
)
535 char itemPartCode
[2];
536 itemPartCode
[0] = itemPart
;
537 itemPartCode
[1] = '\0';
538 if ( getCraftPartForProp( i
).find( itemPartCode
) != string::npos
)
545 bool existsInEcosystem( TEcosystem iEcosystem
, TStatQuality statQuality
) const;
548 static bool mustGenerateFamily( uint iFamily
);
552 const uint ITEM_PART_JEWEL_GEM
= 17; // R
558 struct CFaberCharacteristics
560 /// Default constructor (for reading)
561 CFaberCharacteristics()
562 : FaberElement(~0), ActualEnergy(0.0f
), ActualOriginality(0.0f
)
564 for ( uint32 i
=0; i
!=NbCharacs
; ++i
)
569 void serial( NLMISC::IStream
& s
)
571 s
.serial( FaberElement
);
572 s
.serial( ActualEnergy
);
573 s
.serial( ActualOriginality
);
574 for ( uint32 c
=0; c
!=NbCharacs
; ++c
)
575 s
.serial( Values
[c
] );
579 void initFaberElement( uint32 rFaberElement
) { FaberElement
= rFaberElement
; }
581 /// Returns false if the RM must NOT be generated.
582 bool randomizeValues( TFaberInterestLevel interestLevel
, TFaberInterestLevel nbInterestLevels
, uint iVariant
, float widthRatio
, float peakOccurRatio
, float baseBoost
, TEcosystem iEcosystem
, uint iFreq
, uint iFamily
);
584 /// Returns -1 if the RM must NOT be generated, otherwise return the stat average
585 sint32
computeValues( TStatQuality statQuality
, const TFamInfo
& famInfo
, sint remarkableIndicesSetBaseIndex
,
586 TEcosystem iEcosystem
, uint iFreq
, uint iFamily
);
589 void calcQualitativeValues();
591 /// Index of faber element in "MpParam"
595 float Values
[NbCharacs
];
597 /// Average of the actual interest of the random values between 0 and 1
601 float ActualOriginality
;
605 // Returns false if the RM must NOT be generated.
606 //bool randomizeJewelProtection( TStatQuality statQuality, TEcosystem iEcosystem, uint iFreq, uint iFamily );
608 void computeJewelProtection( TStatQuality statQuality
, TEcosystem iEcosystem
, uint iFamily
);
613 void CFaberCharacteristics::calcQualitativeValues()
615 float actualInterests
[NbCharacs
];
617 // Calculate the average of ratio between [0,1]
618 float sumActualInterest
= 0.0f
;
619 uint nbCharacsUsed
= 0;
620 for ( uint r
=0; r
!=NbCharacs
; ++r
)
622 if ( MaxCharacValues
[r
] == 0 )
624 if ( ! CharacSlotFilter
[r
][FaberElement
] )
627 float interestRatio
= (Values
[r
] / PeakCharacValues
[r
]); // new: Peak is taken as the max => the Energy is in [0..100]
628 if ( ! PositiveCharacs
[r
] )
629 interestRatio
= 1.0f
- interestRatio
; // thus, can be negative
631 actualInterests
[r
] = interestRatio
;
632 sumActualInterest
+= interestRatio
;
636 if ( nbCharacsUsed
== 0 )
639 ActualEnergy
= (sumActualInterest
/ (float)nbCharacsUsed
);
640 if ( ActualEnergy
> 1.0f
)
643 // Calculate the standard deviation (SQRT(SUM((Ai-Aavg)^2)/N))
644 float varianceSum
= 0.0f
;
645 for ( uint r
=0; r
!=NbCharacs
; ++r
)
647 if ( MaxCharacValues
[r
] == 0 )
649 if ( ! CharacSlotFilter
[r
][FaberElement
] )
652 varianceSum
+= sqr( actualInterests
[r
] - ActualEnergy
);
655 // Don't normalize standard deviation, otherwise low energy materials will be considered more
656 // original (by average) than high energy materials. They wouldn't be comparable.
658 //if ( ActualEnergy != 0.0f )
659 ActualOriginality
= (float)sqrt( (double)(varianceSum
/ (float)nbCharacsUsed
) ); // / ActualEnergy;
661 // nlinfo( "Null energy for craft slot %u", FaberElement );
666 * Fill childrenToGet if rootNameForGetChildren is not null
668 void fillFromDFN( UFormLoader
*formLoader
, map
<string
, CDfnFieldInfo
>& dfnFields
, UFormDfn
*formDfn
, const string
& rootName
, const string
& dfnFilename
,
669 const char *rootNameForGetChildren
=NULL
, vector
<string
>& childrenToGet
=vector
<string
>() )
672 for ( i
=0; i
!=formDfn
->getNumEntry(); ++i
)
674 string entryName
, rootBase
;
675 formDfn
->getEntryName( i
, entryName
);
676 rootBase
= rootName
.empty() ? "" : (rootName
+".");
678 UFormDfn::TEntryType entryType
;
680 formDfn
->getEntryType( i
, entryType
, array
);
683 case UFormDfn::EntryVirtualDfn
:
685 CSmartPtr
<UFormDfn
> subFormDfn
= formLoader
->loadFormDfn( (entryName
+ ".dfn").c_str() );
687 nlwarning( "Can't load virtual DFN %s", entryName
.c_str() );
689 fillFromDFN( formLoader
, dfnFields
, subFormDfn
, rootBase
+ entryName
, entryName
+ ".dfn", rootNameForGetChildren
, childrenToGet
); // recurse
692 case UFormDfn::EntryDfn
: // .dfn
694 UFormDfn
*subFormDfn
;
695 if ( formDfn
->getEntryDfn( i
, &subFormDfn
) )
698 formDfn
->getEntryFilename( i
, filename
);
699 fillFromDFN( formLoader
, dfnFields
, subFormDfn
, rootBase
+ entryName
, filename
, rootNameForGetChildren
, childrenToGet
); // recurse
701 if ( rootNameForGetChildren
&& (rootName
== rootNameForGetChildren
) )
703 childrenToGet
.push_back( rootBase
+ entryName
);
707 case UFormDfn::EntryType
: // .typ
709 vector
<string
> values
, labels
;
711 if ( formDfn
->getEntryType( i
, &subType
) )
713 uint32 listSize
= subType
->getNumDefinition();
717 for ( uint32 j
=0; j
!=listSize
; ++j
)
719 subType
->getDefinition( j
, label
, value
);
720 if ( (subType
->getIncrement() == "1") && (subType
->getType() == UType::UnsignedInt
|| subType
->getType() == UType::SignedInt
) )
722 // Fill blank entry for skipped identifier values (to allow indexing by identifier value)
723 sint num
= atoi( value
.c_str() );
724 while ( num
- (sint
)values
.size() > 0 )
726 values
.push_back( "" );
727 labels
.push_back( "" );
730 values
.push_back( value
);
731 labels
.push_back( label
);
735 dfnFields
.insert( make_pair( rootBase
+ entryName
, CDfnFieldInfo(values
, labels
) ) );
736 //nlinfo( "DFN entry: %s (in %s)", (rootBase + entryName).c_str(), dfnFilename.c_str() );
747 CForm
*loadTemplateForm( UFormLoader
*formLoader
, const string
& sheetType
)
749 CForm
*form
= (CForm
*)formLoader
->loadForm( (string("_empty.")+sheetType
).c_str() );
751 nlerror( "Can't load sheet _empty.%s", sheetType
.c_str() );
759 void eraseCarriageReturns( string
& s
)
761 const char CR
= '\n';
762 string::size_type p
= s
.find( CR
);
763 while ( (p
=s
.find( CR
)) != string::npos
)
771 string
getNomenclatureCode( const string
& longName
, set
<string
>& usedCodes
, uint32 nbLetters
)
773 if ( nbLetters
> longName
.size() )
774 nlerror( "Wrong nbLetters for %s", longName
.c_str() );
776 // Start with beginning of name
777 string code
= strlwr(longName
.substr( 0, nbLetters
));
778 uint32 i
= nbLetters
-1;
779 while ( usedCodes
.find( code
) != usedCodes
.end() )
782 if ( i
< longName
.size() )
784 // Substitute last code char by a char from the name (except ' ')
785 if ( longName
[i
] != ' ' )
786 code
[nbLetters
-1] = tolower(longName
[i
]);
792 // If no char from the name is suitable, increment the last char of the code until suitable
794 while ( usedCodes
.find( code
) != usedCodes
.end() )
796 code
[nbLetters
-1] = tolower(longName
[nbLetters
-1]) + c
;
799 nlerror( "Impossible to make code for %s", longName
.c_str() );
804 usedCodes
.insert( code
);
810 * Displays mapping if title not null.
812 void buildNomenclatureCodes( const char *title
, const vector
<string
>& longNames
, vector
<string
>& codes
, uint32 nbLetters
)
814 set
<string
> usedCodeSet
;
816 for ( i
=0; i
!=longNames
.size(); ++i
)
818 codes
[i
] = getNomenclatureCode( longNames
[i
], usedCodeSet
, nbLetters
);
820 nlinfo( "%s %s -> %s", title
, longNames
[i
].c_str(), codes
[i
].c_str() );
821 //DebugLog->displayRawNL( "%s", longNames[i].c_str() );
827 * Set the size of a family code to NB_FAMILY_CODE_CHARS
829 void normalizeFamilyCode( std::string
& s
)
831 if ( s
.size() > NB_FAMILY_CODE_CHARS
)
833 nlerror( "Family codes limited to %u chars (%s)", NB_FAMILY_CODE_CHARS
, s
.c_str() );
838 while ( p
< NB_FAMILY_CODE_CHARS
)
850 void loadNomenclatureCodes( const char *title
, const vector
<string
>& longNames
, vector
<string
>& codes
, const char *filename
)
852 if ( longNames
.empty() )
854 nlwarning( "No nomenclature codes to load. %s", title
? title
: "" );
857 codes
.resize( longNames
.size() );
859 char lineBuffer
[2048];
861 const char *SEPARATOR
= ";";
863 vector
<string
>::iterator iarg
;
864 vector
<string
>::const_iterator ivs
;
866 if ( (rulesFile
= NLMISC::nlfopen( filename
, "r" )) == NULL
)
868 nlwarning( "Can't find file %s", filename
);
872 while ( ! feof(rulesFile
) )
875 fgets( lineBuffer
, 2048, rulesFile
);
876 explode( lineBuffer
, SEPARATOR
, args
);
878 // Get rid of carriage returns!
879 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
881 eraseCarriageReturns( *iarg
);
885 const uint32 MIN_COLS
= 6;
886 const uint32 NAME_COL
= 4;
887 const uint32 C_COL
= 2;
888 const uint32 R_COL
= 3;
889 const uint32 NB_CODE_CHARS
= 2;
890 if ( (args
.size()>=MIN_COLS
) && (! args
[0].empty()) && (args
[0].find( "name" )==string::npos
) ) // skip blank lines, and lines with blank header or "name" in the first column
892 if ( args
[NAME_COL
].empty() )
895 ivs
= find( longNames
.begin(), longNames
.end(), args
[NAME_COL
] );
896 if ( ivs
== longNames
.end() )
897 nlwarning( "Name %s is not in the names array", args
[NAME_COL
].c_str() );
900 string code
= args
[C_COL
] + args
[R_COL
];
901 if ( code
.size() < NB_CODE_CHARS
)
903 nlwarning( "Invalid partial code for %s: %s", (*ivs
).c_str(), code
.c_str() );
906 else if ( code
.size() > NB_CODE_CHARS
)
908 nlinfo( "Compacting code '%s' for %s", code
.c_str(), (*ivs
).c_str() );
910 while ( (p
= code
.find( ' ' )) != string::npos
)
916 if ( codes
[ivs
-longNames
.begin()].empty() )
919 nlinfo( "%s %s -> %s", title
, (*ivs
).c_str(), code
.c_str() );
920 codes
[ivs
-longNames
.begin()] = code
;
924 if ( code
!= codes
[ivs
-longNames
.begin()] )
925 nlwarning( "Invalid nomenclature: (%s and %s for %s: ", codes
[ivs
-longNames
.begin()].c_str(), code
.c_str(), (*ivs
).c_str() );
931 for ( ivs
=codes
.begin(); ivs
!=codes
.end(); ++ivs
)
933 if ( (*ivs
).empty() )
934 nlwarning( "No code found for %s", (*(longNames
.begin() + (ivs
- codes
.begin()))).c_str() );
943 inline sint
getLastUsedPropertySlot( uint32
*iProperties
, sint lastPropertySlot
, uint32 undefinedProperty
)
945 for ( sint r
=lastPropertySlot
; r
>=0; --r
)
947 if ( iProperties
[r
] != undefinedProperty
)
957 inline bool passNegativeFilter( const vector
<uint32
>& incompatibilityList
, uint32 iValue
)
959 vector
<uint32
>::const_iterator ip
= find( incompatibilityList
.begin(), incompatibilityList
.end(), iValue
);
960 return (ip
== incompatibilityList
.end());
967 inline bool passPositiveFilter( const vector
<uint32
>& compatibilityList
, uint32 iValue
)
969 vector
<uint32
>::const_iterator ip
= find( compatibilityList
.begin(), compatibilityList
.end(), iValue
);
970 return (ip
!= compatibilityList
.end());
975 * Reject a prop if it is in the incompatibility list of a family
977 bool passPropFamilyFilter( const vector
<uint32
>& iFamilyRelatedProperties
, uint32 iProp
)
979 return passNegativeFilter( iFamilyRelatedProperties
, iProp
);
984 * Reject a creature if NOT in the creature list of a family
986 /*bool passCreatureFilter( const vector<uint32>& iFamilyRelatedCreatures, uint32 iCreature )
988 //nldebug( "%u related creatures, %s", iFamilyRelatedCreatures.size(), passPositiveFilter( iFamilyRelatedCreatures, iCreature ) ? "TRUE": "FALSE" );
989 return passPositiveFilter( iFamilyRelatedCreatures, iCreature );
996 class CStrIComparator
: public binary_function
<string
, string
, bool>
999 bool operator() ( const string
& s1
, const string
& s2
) const
1001 return (nlstricmp( s1
, s2
) == 0);
1009 void displayList( const vector
<string
>& v
, CLog
*log
=DebugLog
)
1011 vector
<string
>::const_iterator ist
;
1012 for ( ist
=v
.begin(); ist
!=v
.end(); ++ist
)
1013 log
->displayRaw( "%s ", (*ist
).c_str() );
1014 log
->displayRawNL( "" );
1021 uint32
getIndexFromString( const string
& s
, const vector
<string
>& v
, bool displayWarning
=true )
1025 if ( displayWarning
)
1026 nlwarning( "Can't find '%s' in empty array", s
.c_str() );
1031 vector
<string
>::const_iterator ist
= find_if( v
.begin(), v
.end(), bind2nd(CStrIComparator(), s
) );
1032 if ( ist
== v
.end() )
1034 if ( displayWarning
)
1036 nlwarning( "Can't find '%s' in:", s
.c_str() );
1037 displayList( v
, WarningLog
);
1042 return ist
- v
.begin();
1050 uint32
getIndexFromString( const string
& s
, const char **array
, uint arraySize
, bool displayWarning
=true )
1052 if ( arraySize
== 0 )
1054 if ( displayWarning
)
1055 nlwarning( "Can't find '%s' in empty array", s
.c_str() );
1060 for ( uint i
=0; i
!=arraySize
; ++i
)
1062 if ( strlwr(string(array
[i
])) == strlwr(s
) )
1066 if ( displayWarning
)
1068 nlwarning( "Can't find '%s' in:", s
.c_str() );
1069 //displayList( v, WarningLog );
1077 * Returns the index of the erased element, ~0 if not found
1079 uint32
removeEntryFromList( vector
<string
>& v
, const string
& entry
)
1081 vector
<string
>::iterator ivs
;
1082 ivs
= find( v
.begin(), v
.end(), entry
);
1084 if ( ivs
!= v
.end() )
1086 index
= ivs
- v
.begin();
1098 bool removeEntryFromListByIndex( vector
<string
>& v
, uint32 index
)
1100 if ( index
< v
.size() )
1102 v
.erase( v
.begin() + index
);
1110 typedef void (*TMapDeliveryCallback
) ( mss
& );
1111 typedef void (*TVectorDeliveryCallback
) ( vs
& );
1117 void loadCSVFile( const char *filename
, TMapDeliveryCallback deliveryCallback
, bool firstColWithoutName
=false )
1119 char lineBuffer
[2048];
1121 const char *SEPARATOR
= ";";
1122 vector
<string
> args
;
1123 vector
<string
>::iterator iarg
;
1125 if ( (file
= NLMISC::nlfopen( filename
, "r" )) == NULL
)
1127 nlwarning( "Can't find file %s", filename
);
1131 // Read first line as header with column names
1132 lineBuffer
[0] = '\0';
1133 fgets( lineBuffer
, 2048, file
);
1134 explode( lineBuffer
, SEPARATOR
, args
);
1136 // Store column names (and get rid of carriage returns!)
1137 vector
< string
> columnNames
;
1139 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
1141 if ( firstColWithoutName
&& (iarg
== args
.begin()) )
1143 *iarg
= "<>"; // override column name for the 1st column
1145 eraseCarriageReturns( *iarg
);
1146 columnNames
.push_back( *iarg
);
1147 valuesByName
.insert( make_pair( *iarg
, string("") ) );
1150 while ( ! feof(file
) )
1153 lineBuffer
[0] = '\0';
1154 fgets( lineBuffer
, 2048, file
);
1155 explode( lineBuffer
, SEPARATOR
, args
);
1157 // Set values (and get rid of carriage returns!)
1158 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
1160 eraseCarriageReturns( *iarg
);
1161 valuesByName
[columnNames
[iarg
-args
.begin()]] = *iarg
;
1164 // Deliver the wanted fields
1165 deliveryCallback( valuesByName
);
1174 void loadCSVFile( const char *filename
, TVectorDeliveryCallback deliveryCallback
)
1176 char lineBuffer
[2048];
1178 const char *SEPARATOR
= ";";
1182 if ( (file
= NLMISC::nlfopen( filename
, "r" )) == NULL
)
1184 nlwarning( "Can't find file %s", filename
);
1188 while ( ! feof(file
) )
1191 lineBuffer
[0] = '\0';
1192 fgets( lineBuffer
, 2048, file
);
1193 explode( lineBuffer
, SEPARATOR
, args
);
1195 // Get rid of carriage returns!
1196 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
1198 eraseCarriageReturns( *iarg
);
1201 // Deliver the wanted fields
1202 deliveryCallback( args
);
1211 void loadValueFile( const char *filename
, const vector
<string
>& keyStrings
,
1212 vector
<sint
>& contents
, sint defaultValue
)
1214 nlassert( keyStrings
.size() == contents
.size() );
1215 char lineBuffer
[2048];
1217 const char *SEPARATOR
= ";";
1218 vector
<string
> args
;
1219 vector
<string
>::iterator iarg
;
1221 if ( (rulesFile
= NLMISC::nlfopen( filename
, "r" )) == NULL
)
1223 nlwarning( "Can't find file %s", filename
);
1227 while ( ! feof(rulesFile
) )
1230 lineBuffer
[0] = '\0';
1231 fgets( lineBuffer
, 2048, rulesFile
);
1232 explode( lineBuffer
, SEPARATOR
, args
);
1234 // Get rid of carriage returns!
1235 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
1237 eraseCarriageReturns( *iarg
);
1241 if ( (! args
.empty()) && (! args
[0].empty()) ) // skip blank lines, and lines with blank header
1243 sint value
= defaultValue
;
1244 for ( uint32 a
=0; a
!=args
.size()-1; ++a
)
1246 if ( ! args
[a
+1].empty() ) // skip blank entries
1247 value
= atoi( args
[a
+1].c_str() );
1249 uint32 index
= getIndexFromString( args
[0], keyStrings
);
1252 contents
[index
] = value
;
1256 fclose( rulesFile
);
1264 void loadRulesFile( const char *filename
, const vector
<string
>& keyStrings
,
1265 const vector
<string
>& contentStrings
, CRulesFilter
& filter
,
1266 const string
& matchExtKeyAtFirstColumn
=string() )
1268 char lineBuffer
[2048];
1270 const char *SEPARATOR
= ";";
1271 uint32 firstColumn
= matchExtKeyAtFirstColumn
.empty() ? 0 : 1;
1272 vector
<string
> args
;
1273 vector
<string
>::iterator iarg
;
1275 if ( (rulesFile
= NLMISC::nlfopen( filename
, "r" )) == NULL
)
1277 nlwarning( "Can't find file %s", filename
);
1281 while ( ! feof(rulesFile
) )
1284 lineBuffer
[0] = '\0';
1285 fgets( lineBuffer
, 2048, rulesFile
);
1286 explode( lineBuffer
, SEPARATOR
, args
);
1288 // Get rid of carriage returns!
1289 for ( iarg
=args
.begin(); iarg
!=args
.end(); ++iarg
)
1291 eraseCarriageReturns( *iarg
);
1294 // Match with ext key string if set
1295 if ( (! matchExtKeyAtFirstColumn
.empty()) && (args
[0]!=matchExtKeyAtFirstColumn
) )
1299 if ( (! args
.empty()) && (! args
[firstColumn
].empty()) ) // skip blank lines, and lines with blank header
1301 vector
<uint32
> contents
;
1302 for ( uint32 a
=firstColumn
; a
!=args
.size()-1; ++a
)
1304 if ( ! args
[a
+1].empty() ) // skip blank entries
1305 contents
.push_back( getIndexFromString( args
[a
+1], contentStrings
) );
1307 filter
.insert( make_pair( getIndexFromString( args
[firstColumn
], keyStrings
), contents
) );
1310 fclose( rulesFile
);
1316 * 1st column: extKeyStrings (corresponding to the filters 'vector'); 2nd: keyStrings
1318 void loadRulesFileMulti( const char *filename
, const vector
<string
>& extKeyStrings
, const vector
<string
>& keyStrings
, const vector
<string
>& contentStrings
, vector
<CRulesFilter
>& filters
)
1320 filters
.resize( extKeyStrings
.size() );
1321 for ( uint32 i
=0; i
!=filters
.size(); ++i
)
1323 loadRulesFile( filename
, keyStrings
, contentStrings
, filters
[i
], extKeyStrings
[i
] );
1324 /*CRulesFilter::const_iterator irf;
1325 nldebug( "%s", extKeyStrings[i].c_str() );
1326 for ( irf=filters[i].begin(); irf!=filters[i].end(); ++irf )
1328 nldebug( "%s", keyStrings[(*irf).first].c_str() );
1329 vector<uint32>::const_iterator ivi;
1330 for ( ivi=(*irf).second.begin(); ivi!=(*irf).second.end(); ++ivi )
1332 nldebug( "%u", *ivi );
1340 * Clear the form to reuse it (and all contents below node)
1342 void clearSheet( CForm
*form
, UFormElm
* node
)
1344 ((CFormElm
*)node
)->clean();
1350 * Saves to disk if bool WriteSheetsToDisk is true
1352 void flushSheetToDisk( const string
& fullFilename
, UForm
*form
)
1354 if ( WriteSheetsToDisk
)
1356 COFile
output( fullFilename
);
1357 form
->write( output
, false );
1365 string::size_type
findCapital( const string
& s
, string::size_type startPos
)
1367 string::size_type p
;
1368 for ( p
=startPos
; p
!=s
.size(); ++p
)
1370 if ( (s
[p
] >= 'A') && (s
[p
] <= 'Z') )
1373 return string::npos
;
1378 * Transform "MyString " into "My string"
1380 void detachValue( string
& s
)
1385 string::size_type p
;
1386 while ( (p
= findCapital( s
, 1 )) != string::npos
)
1389 s
[p
+1] = tolower( s
[p
+1] );
1392 // Rip off any blank at the end
1393 if ( s
[s
.size()-1] == ' ' )
1395 s
.resize( s
.size()-1 );
1403 void getTransposedMap( CRulesFilter
& dest
, const CRulesFilter
& src
)
1405 CRulesFilter::const_iterator im
;
1406 for ( im
=src
.begin(); im
!=src
.end(); ++im
)
1408 vector
<uint32
>::const_iterator iv
;
1409 for ( iv
=(*im
).second
.begin(); iv
!=(*im
).second
.end(); ++iv
)
1411 dest
[*iv
].push_back( (*im
).first
);
1420 string
makeFaberElementCode( uint32 iFaberElement
, TFaberInterestLevel level
, TFaberInterestLevel nbInterestLevels
)
1422 return toString( "%c%d", 'a' + iFaberElement
, getNomenclaturedInterestLevel( level
, nbInterestLevels
) );
1429 inline bool hasMatchingFaberLevel( TFaberInterestLevel storedLevel
, TFaberInterestLevel submittedLevel
)
1431 return storedLevel
<= submittedLevel
;
1438 //void keepOnlyHighestLevel( vector<CFaberCombination*>& codes )
1440 // nlassert( ! codes.empty() );
1441 // sint maxLevel = -1;
1443 // for ( i=0; i!=codes.size(); ++i )
1445 // if ( codes[i]->FirstLevel > maxLevel )
1447 // maxLevel = codes[i]->FirstLevel;
1450 // vector<CFaberCombination*> remainingCodes;
1451 // for ( i=0; i!=codes.size(); ++i )
1453 // if ( codes[i]->FirstLevel == maxLevel )
1454 // remainingCodes.push_back( codes[i] );
1457 // //nldebug( "%u codes, highest level = %u with %u occurences", codes.size(), maxLevel, remainingCodes.size() );
1458 // //nlassert( remainingCodes.size() <= codes.size() );
1459 // codes = remainingCodes;
1460 // nlassert( ! codes.empty() );
1467 bool allIncludedIn( const vu
& subset
, const vu
& bigset
)
1469 vu::const_iterator iv
;
1470 for ( iv
=subset
.begin(); iv
!=subset
.end(); ++iv
)
1472 if ( find( bigset
.begin(), bigset
.end(), *iv
) == bigset
.end() )
1482 void loadConfigFlag( CConfigFile
& configFile
, const char *varTitle
, bool &flag
)
1484 CConfigFile::CVar
*var
= configFile
.getVarPtr( varTitle
);
1486 flag
= (var
->asInt() == 1);
1493 string::size_type
getCapitalFromPos( const string
& s
, string::size_type startPos
)
1495 //nldebug( "%s %u", s.c_str(), startPos );
1496 string::size_type p
;
1497 for ( p
=startPos
; p
<s
.size(); ++p
)
1499 if ( (s
[p
] >= 'A') && (s
[p
] <= 'Z') )
1502 return string::npos
;
1507 * Also used to make system_mp filenames.
1508 * Converts "My Identifier" or "MyIdentifier" to "my_identifier" ("My identifier" to "Myidentifier")
1510 string
conventionalDirectory( const string
& dirname
)
1512 if ( dirname
.empty() )
1515 string result
= dirname
;
1518 string::size_type p
= 0;
1519 while ( (p
= result
.find( ' ' )) != string::npos
)
1521 result
.erase( p
, 1 );
1524 // Convert capitals to underscores
1525 result
[0] = tolower( result
[0] );
1527 while ( (p
= getCapitalFromPos( result
, p
)) != string::npos
)
1529 result
.insert( p
, "_" );
1531 result
[p
] = tolower( result
[p
] );
1537 mss UniqueRMNamesAndSheetCodeHead
;
1543 void readRMNames( mss
& values
)
1545 string
& name
= values
["basics.name"];
1546 if ( ! name
.empty() )
1548 string radix
= values
["FILE"].substr( 0, 5 );
1549 UniqueRMNamesAndSheetCodeHead
.insert( make_pair( name
, radix
) );
1557 void loadTitles( const string
& sourceWords
, const string
& sourceBase
, const string
& languageCode
, CTitles
& dest
)
1559 STRING_MANAGER::TWorksheet worksheet
;
1560 STRING_MANAGER::loadExcelSheet( TranslationPath
+ sourceBase
+ "/" + sourceWords
+ "_words_" + languageCode
+ ".txt", worksheet
);
1561 uint cp
, cn
, nbTitles
= 0;
1562 if ( worksheet
.findCol( ucstring(sourceWords
+ " ID"), cp
) && worksheet
.findCol( ucstring("name"), cn
) )
1564 for ( std::vector
<STRING_MANAGER::TWorksheet::TRow
>::iterator ip
= worksheet
.begin(); ip
!=worksheet
.end(); ++ip
)
1566 if ( ip
== worksheet
.begin() ) // skip first row
1568 STRING_MANAGER::TWorksheet::TRow
& row
= *ip
;
1569 dest
.insert( make_pair( row
[cp
].toString(), row
[cn
].toUtf8() ) );
1574 nlwarning( "%s ID or name not found", sourceWords
.c_str() );
1576 nlinfo( "Loaded %u %s titles", nbTitles
, sourceWords
.c_str() );
1583 void extractRawMaterialNames()
1585 loadCSVFile( ExtractNamesCsv
.c_str(), readRMNames
);
1586 FILE *output
= NLMISC::nlfopen( CFile::getFilenameWithoutExtension( ExtractNamesCsv
) + "_output.csv", "wt" );
1587 fprintf( output
, "Code;Name\n" );
1588 for ( mss::const_iterator iun
=UniqueRMNamesAndSheetCodeHead
.begin(); iun
!=UniqueRMNamesAndSheetCodeHead
.end(); ++iun
)
1590 const string
& codeRadix
= (*iun
).second
;
1591 const string
& name
= (*iun
).first
;
1592 fprintf( output
, "%s;%s\n", codeRadix
.c_str(), name
.c_str() );
1600 void cleanExteriorWhitespace( vs
& line
)
1602 for ( vs::iterator it
=line
.begin(); it
!=line
.end(); ++it
)
1604 CSString
& s
= (*it
);
1605 string::size_type p
;
1606 for ( p
=0; p
!=s
.size(); ++p
)
1611 if ( (p
!= 0) && (p
!= s
.size()) )
1614 for ( p
=0; p
!=s
.size(); ++p
)
1616 if ( s
[s
.size()-1-p
] != ' ' )
1619 if ( (p
!= 0) && (p
!= s
.size()) )
1620 s
= s
.rightCrop( p
);
1625 uint
TFamInfo::UseGenerateOnly
= 0;
1627 // Only used for deposits; for creature, works with the creature sheets found
1628 /*bool TFamInfo::existsInEcosystem( TEcosystem iEcosystem, TStatQuality statQuality ) const
1630 switch ( iEcosystem )
1632 case CommonEcosystem: // The Common rm exists if the rm family has a freq=2 (or 0 but only in Supreme)
1633 return ( (find( Freqs.begin(), Freqs.end(), 2 ) != Freqs.end())
1634 || ((find( Freqs.begin(), Freqs.end(), 0 ) != Freqs.end()) && (statQuality == Supreme)) );
1635 // was: find_if ... bind2nd( equals<uint>(), 1 )
1637 case PrimeRoots: // Only freq 1 families exist if the PrimeRoots
1638 return find( Freqs.begin(), Freqs.end(), 1 ) != Freqs.end();
1640 default: // A rm family exists in the ecosystem matching a civ if the corresponding freq is 1 or 3
1642 uint iCiv = getIndexFromString( ecosystemCodes[iEcosystem], CivEcosystemCodes, NbCiv, false );
1643 vector<TCiv>::const_iterator it = find( Civs.begin(), Civs.end(), (TCiv)iCiv );
1644 if ( it != Civs.end() )
1645 return (Freqs[it-Civs.begin()] == 1) || (Freqs[it-Civs.begin()] == 3);
1652 // Only used for deposits;
1653 bool TFamInfo::existsInEcosystem( TEcosystem iEcosystem
, TStatQuality statQuality
) const
1655 if ( find( Freqs
.begin(), Freqs
.end(), 0 ) != Freqs
.end() )
1657 // Freq 0 => only Common/Supreme
1658 return (statQuality
== Supreme
) && (iEcosystem
== CommonEcosystem
);
1660 else if ( statQuality
<= Fine
)
1662 // Basic, Fine => Common
1663 return (iEcosystem
== CommonEcosystem
);
1667 // Choice to Supreme => One per ecosystem
1668 return (iEcosystem
!= CommonEcosystem
) && (iEcosystem
< NbEcosystems
);
1676 struct TCraftPartInfo
1693 CCraftParts() : CraftParts( NbFaberElements
)
1695 for ( uint i
=0; i
!=NbFaberElements
; ++i
)
1697 CraftParts
[i
].PartIndex
= i
;
1698 CraftParts
[i
].Enabled
= false;
1703 void registerPartChars( const CSString
& parts
)
1705 for ( string::size_type p
=0; p
!=parts
.size(); ++p
)
1707 uint index
= (uint
)(parts
[p
] - 'A');
1708 CraftParts
[index
].Enabled
= true;
1713 bool isEnabled( uint index
) const
1715 return CraftParts
[index
].Enabled
;
1719 void getNamesAndPaths( const vector
<string
>& paths
)
1722 vector
<string
>::const_iterator ip
;
1723 for ( ip
=paths
.begin(); ip
!=paths
.end(); ++ip
)
1725 if ( i
>= CraftParts
.size() )
1726 nlerror( "Mismatch between sitem DFN and constant (nb of craft parts)" );
1728 CraftParts
[i
].Path
= (*ip
);
1729 string::size_type p
= (*ip
).rfind( '.' ) + 1; // string::npos+1 gives 0
1730 CraftParts
[i
].Name
= (*ip
).substr( p
);
1731 nldebug( "%u: %s", ip
-paths
.begin(), CraftParts
[i
].Name
.c_str() );
1737 TCraftPartInfo
& operator[] ( uint index
) { return CraftParts
[index
]; }
1739 vector
< TCraftPartInfo
> CraftParts
;
1743 typedef map
<CSString
, TFamInfo
, CUnsensitiveSStringLessPred
> CFamMap
;
1745 CCraftParts CraftParts
;
1747 enum TFamAndPropLine
{
1748 LFam
, LGroup
, LCraftParts
, LCiv
, LFreq
, LLoc
,
1749 LIconMain
, LIconBk
, LIconOv1
, LIconOv2
, LIconSpecial
,
1750 LCraftPlans
, LGenerateOnly
,
1751 LBaseOfRemarkableStatIndices
,
1752 LColorIndex
= LBaseOfRemarkableStatIndices
+ NB_REMARKABLE_STAT_INDICES
,
1758 bool TFamInfo::mustGenerateFamily( uint iFamily
)
1760 if ( families
[iFamily
].empty() )
1762 else if ( ! TFamInfo::UseGenerateOnly
)
1766 TFamInfo
& famInfo
= FamSet
[families
[iFamily
]];
1767 return ( famInfo
.GenerateOnly
);
1775 CSString
getShortFaberElemString( uint rFaberElem
)
1777 string
& longString
= CraftParts
[rFaberElem
].Name
;
1778 return reinterpret_cast<CSString
&>(longString
.substr( longString
.find( "(" ) + 1 )).rightCrop( 1 );
1785 TCiv
getCivFromStr( const CSString
& civStr
)
1787 if ( civStr
.empty() )
1791 for ( uint i
=0; i
!=NbCiv
; ++i
)
1793 if ( civStr
== CSString(CivNames
[i
]) )
1796 nlwarning( "Unknown civ '%s'", civStr
.c_str() );
1805 uint
getFreqFromStr( const CSString
& freqStr
)
1807 uint f
= atoi( freqStr
.c_str() );
1808 if ( (f
< 1) && (f
> 5) )
1809 nlwarning( "Unknown freq '%s'", freqStr
.c_str() );
1815 * Returns ~0 if s is empty
1817 TGroup
getNewOrExistingGroupFromStr( const CSString
& s
)
1819 uint i
= getIndexFromString( s
, groups
, false );
1827 groups
.push_back( s
);
1828 nlinfo( "New group: %s (%u)", s
.c_str(), i
);
1838 void deliverFamAndProp( vs
& line
)
1840 if ( line
.size() < NbFamAndPropCols
)
1841 line
.resize( NbFamAndPropCols
);
1843 cleanExteriorWhitespace( line
);
1845 if ( line
[LFam
].empty() )
1847 // Load special icons
1848 if ( (line
.size() >= LIconSpecial
+1) && (! line
[LIconSpecial
].empty()) && (line
[LIconMain
].empty()) )
1850 /*if ( line.size() >= LIconMain+1 )
1851 Icons[line[LIconSpecial]].Icon = line[LIconMain];*/
1852 if ( line
.size() >= LIconBk
+1 )
1853 Icons
[line
[LIconSpecial
]].IconBackground
= line
[LIconBk
];
1854 if ( line
.size() >= LIconOv1
+1 )
1855 Icons
[line
[LIconSpecial
]].IconOver
= line
[LIconOv1
];
1860 // Load icons of families
1861 if ( line
.size() >= LIconMain
+1 )
1863 Icons
[line
[LFam
]].Icon
= line
[LIconMain
];
1864 if ( ! line
[LGroup
].empty() )
1866 // For group, set icon of first family of group found! (for forage source knowledge)
1867 if ( Icons
.find( line
[LGroup
] ) == Icons
.end() )
1869 Icons
[line
[LGroup
]].Icon
= line
[LIconMain
];
1873 if ( line
.size() >= LIconBk
+1 )
1874 Icons
[line
[LFam
]].IconBackground
= line
[LIconBk
];
1875 if ( line
.size() >= LIconOv1
+1 )
1876 Icons
[line
[LFam
]].IconOver
= line
[LIconOv1
];
1878 TFamInfo
& famInfo
= FamSet
[line
[LFam
]];
1879 famInfo
.IsActive
= true;
1880 /*if ( ! line[LCraftParts].empty() )
1882 // Store by property (line[LProp])
1883 famInfo.Properties.push_back( line[LProp] );
1884 famInfo.CraftPartsByProp.push_back( famInfo.CompatibleCraftParts.size() ); // beginning of craft parts chars
1885 famInfo.CompatibleCraftParts += line[LCraftParts];
1886 CraftParts.registerPartChars( line[LCraftParts] );
1887 famInfo.Civs.push_back( getCivFromStr( line[LCiv] ) );
1888 famInfo.Freqs.push_back( getFreqFromStr( line[LFreq] ) );
1889 famInfo.IsInDeposits = line[LLoc].contains( "D" );
1890 famInfo.IsInCreatures = line[LLoc].contains( "C" );
1891 if ( ! (famInfo.IsInDeposits || famInfo.IsInCreatures) )
1892 nlwarning( "Unknown loc for %s", line[LFam].c_str() );
1895 for ( string::size_type p
=0; p
!=line
[LCraftParts
].size(); ++p
)
1897 // Store by property = craft part (each char of line[LCraftParts])
1898 CSString craftPart
= string( 1, line
[LCraftParts
][p
]);
1899 famInfo
.Properties
.push_back( craftPart
);
1900 famInfo
.CraftPartsByProp
.push_back( famInfo
.CompatibleCraftParts
.size() );
1901 famInfo
.CompatibleCraftParts
+= craftPart
;
1902 CraftParts
.registerPartChars( craftPart
);
1903 famInfo
.Civs
.push_back( getCivFromStr( line
[LCiv
] ) );
1904 famInfo
.Freqs
.push_back( getFreqFromStr( line
[LFreq
] ) );
1906 if ( line
[LCraftParts
].empty() )
1908 famInfo
.Freqs
.push_back( getFreqFromStr( line
[LFreq
] ) ); // freq needed for Rarity computation
1910 famInfo
.Group
= getNewOrExistingGroupFromStr( line
[LGroup
] );
1911 famInfo
.IsInDeposits
= line
[LLoc
].contains( "D" );
1912 famInfo
.IsInCreatures
= line
[LLoc
].contains( "C" );
1913 if ( ! (famInfo
.IsInDeposits
|| famInfo
.IsInCreatures
) )
1915 famInfo
.SpecialCreatureTag
= line
[LLoc
];
1916 if ( (famInfo
.SpecialCreatureTag
[0] != 'G') && (famInfo
.SpecialCreatureTag
[0] != 'I') )
1917 nlwarning( "Unknown loc %s for %s", line
[LLoc
].c_str(), line
[LFam
].c_str() );
1919 famInfo
.IsForMission
= line
[LCraftParts
].empty();
1920 for ( uint i
=0; i
!=NB_REMARKABLE_STAT_INDICES
; ++i
)
1922 if ( line
[LBaseOfRemarkableStatIndices
+i
].empty() && (! line
[LCraftParts
].empty()) && (line
[LFreq
] != "0") )
1923 nlerror( "%s has empty stat index %u", line
[LFam
].c_str(), i
);
1924 famInfo
.RemarkableStatIndex
[i
] = atoi( line
[LBaseOfRemarkableStatIndices
+i
].c_str() );
1926 if ( ! line
[LColorIndex
].empty() )
1927 famInfo
.ColorIndex
= atoi( line
[LColorIndex
].c_str() );
1928 if ( ! line
[LJewelProtIndex
].empty() )
1929 famInfo
.JewelProtIndex
= atoi( line
[LJewelProtIndex
].c_str() );
1930 bool markedForGeneration
= (line
[LGenerateOnly
] == "X");
1931 if ( (!markedForGeneration
) && famInfo
.GenerateOnly
)
1933 nlwarning( "Found duplicate family line with different GenerateOnly setting" );
1937 famInfo
.GenerateOnly
= markedForGeneration
;
1939 if ( famInfo
.GenerateOnly
)
1940 ++TFamInfo::UseGenerateOnly
;
1944 typedef map
< TGroup
, set
<uint32
> > CGroupMap
;
1950 void loadFamAndProp( const string
& filename
, bool displayAll
)
1952 loadCSVFile( filename
.c_str(), deliverFamAndProp
);
1956 set
<CSString
, CUnsensitiveSStringLessPred
> propSet
;
1959 /// Generate contents of item_mp_family.typ (and fill group map)
1960 nlinfo( "item_mp_family.typ:" );
1961 InfoLog
->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" );
1963 for ( CFamMap::iterator iss
=FamSet
.begin(); iss
!=FamSet
.end(); ++iss
)
1965 const CSString
& famStr
= (*iss
).first
;
1966 TFamInfo
& famInfo
= (*iss
).second
;
1967 InfoLog
->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr
.c_str(), i
);
1969 // Get info about props and group
1970 for ( vs::iterator ip
=famInfo
.Properties
.begin(); ip
!=famInfo
.Properties
.end(); ++ip
)
1972 propSet
.insert( *ip
);
1974 groupMap
[ famInfo
.Group
].insert( i
); // ~0 is for "no group" (creature's RMs only)
1979 /// Generate family-specialized forage search bricks (TODO)
1980 nlinfo( "Family-specialized forage search bricks:");
1982 for ( CGroupMap::iterator igm=groupMap.begin(); igm!=groupMap.end(); ++igm )
1984 CSString skill = toString( "SHFM%u", i );
1985 CSString rmgrpBrickCode = toString( "BHFPMB%02u", i );
1986 CSString rmfamBrickFamCode = "BHFPMI" + string( 1, (char)'A' + ((char)(i-1)) );
1988 for ( set<uint32>:::iterator ifs=(*igm).begin(); ifs!=(*igm).end(); ++ifs )
1990 // TODO: modifier of modifier
1991 CSString brickCode = rmfamBrickFamCode + toString( "%02u", j );
1992 InfoLog->displayRawNL( "%s\t80\t%s\t%u\t\t%s\t\tFG_RMFAM_FILT: %u\t", brickCode.c_str(), rmgrpBrickCode.c_str(), j, skill.c_str(), (*ifs) );
1997 /// Generate family-specialized forage search phrases (TODO)
1998 nlinfo( "Family-specialized forage search phrases:");
2000 for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss )
2002 const CSString& famStr = (*iss).first;
2003 TFamInfo& famInfo = (*iss).second;
2004 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i );
2007 /// Generate family-specialized forage extraction bricks (TODO)
2008 nlinfo( "Family-specialized forage extraction bricks:");
2010 for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss )
2012 const CSString& famStr = (*iss).first;
2013 TFamInfo& famInfo = (*iss).second;
2014 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i );
2018 /// Generate family-specialized forage extraction phrases (TODO)
2019 nlinfo( "Family-specialized forage extraction phrases:");
2021 for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss )
2023 const CSString& famStr = (*iss).first;
2024 TFamInfo& famInfo = (*iss).second;
2025 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i );
2029 /// Generate item_mp_property.typ
2030 nlinfo( "Item parts as props:" );
2031 InfoLog
->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" );
2033 for ( set
<CSString
, CUnsensitiveSStringLessPred
>::iterator iss
=propSet
.begin(); iss
!=propSet
.end(); ++iss
)
2035 InfoLog
->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss
).c_str(), i
);
2039 /// Generate item_mp_group.typ
2040 nlinfo( "Groups:" );
2041 InfoLog
->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" );
2043 for ( CGroupMap::iterator igm
=groupMap
.begin(); igm
!=groupMap
.end(); ++igm
)
2045 if ( (*igm
).first
!= ~0 )
2047 InfoLog
->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (groups
[(*igm
).first
]).c_str(), i
);
2053 /// Generate group-specialized forage search bricks (TODO)
2054 nlinfo( "Group-specialized forage search bricks:");
2056 for ( CGroupMap::iterator igm=groupMap.begin(); igm!=groupMap.end(); ++igm )
2058 CSString skill = toString( "SHFM%u", i );
2059 CSString rmgrpBrickCode = toString( "BHFPMB%02u", i );
2063 /// Generate group-specialized forage search phrases
2064 nlinfo( "Group-specialized forage search phrases:");
2066 for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss )
2068 if ( (*iss).empty() )
2070 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i );
2074 /// Generate group-specialized forage extraction bricks
2075 nlinfo( "Group-specialized forage extraction bricks:");
2077 for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss )
2079 if ( (*iss).empty() )
2081 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i );
2085 /// Generate group-specialized forage extraction phrases
2086 nlinfo( "Group-specialized forage extraction phrases:");
2088 for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss )
2090 if ( (*iss).empty() )
2092 InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i );
2096 nlinfo( "TODO: Keep old values when adding new entries" );
2097 nlinfo( "Don't forget to regen craft plans and to map localized texts" );
2103 * Multi-indexed array.
2104 * NC is the number of columns.
2106 template <uint32 NC
>
2111 /// A row is made of fields, usually 1 per column but there may be more than one (each one is a key)
2112 struct TSortableItem
2114 std::vector
<std::string
> Fields
[NC
];
2117 void push( uint32 column
, const std::string
& f
, bool allowDuplicates
=false )
2119 if ( (allowDuplicates
) || (find( Fields
[column
].begin(), Fields
[column
].end(), f
) == Fields
[column
].end()) )
2120 Fields
[column
].push_back( f
);
2124 * Display the item as a row of a HTML table.
2125 * If (key!=previousKey) and (name==previousName), the row will not be displayed entirely to save space
2127 * \param keyColumn If not ~0, column used for sorting => this column displays only the field matching the key
2128 * \param key The key used for sorting (see keyColumn)
2129 * \param previousKey Previous key
2130 * \param nameColumn If not ~0, column used for the unique name (column must have exaclty one element)
2131 * \param previousName Previous name
2133 std::string
toHTMLRow( uint32 keyColumn
=~0, const string
& key
=string(), const string
& previousKey
=string(),
2134 uint32 nameColumn
=~0, const string
& previousName
=string() ) const
2136 std::string s
= "<tr>";
2137 bool lightMode
= (nameColumn
== ~0) ? false : ((key
!= previousKey
) && (Fields
[nameColumn
][0] == previousName
));
2138 for ( uint32 c
=0; c
!=NC
; ++c
)
2141 if ( c
== keyColumn
)
2142 s
+= key
; // key should be a substr of toString( c )
2148 s
+= columnToString( c
);
2158 std::string
toCSVLine( char columnSeparator
=',', string internalSeparator
=" - ", uint32 keyColumn
=~0, const string
& key
=string(), const string
& previousKey
=string(),
2159 uint32 nameColumn
=~0, const string
& previousName
=string() ) const
2162 bool lightMode
= (nameColumn
== ~0) ? false : ((key
!= previousKey
) && (Fields
[nameColumn
][0] == previousName
));
2163 for ( uint32 c
=0; c
!=NC
; ++c
)
2165 if ( c
== keyColumn
)
2166 s
+= key
; // key should be a substr of columnToString( c )
2172 s
+= columnToString( c
, internalSeparator
);
2174 s
+= columnSeparator
;
2181 std::string
columnToString( uint32 column
, const std::string
& internalSeparator
=", " ) const
2184 std::vector
<std::string
>::const_iterator ivs
;
2185 for ( ivs
=Fields
[column
].begin(); ivs
!=Fields
[column
].end(); ++ivs
)
2187 if ( ivs
!=Fields
[column
].begin() )
2188 s
+= internalSeparator
;
2195 typedef std::multimap
< std::string
, uint32
> CLookup
; // key to index (not pt because reallocation invalidates pointers)
2196 typedef std::vector
< TSortableItem
> CItems
;
2199 void init( bool enabled
)
2205 void addItem( const TSortableItem
& item
)
2210 _Items
.push_back( item
);
2211 for ( uint32 c
=0; c
!=NC
; ++c
)
2213 for ( std::vector
<std::string
>::const_iterator ik
=item
.Fields
[c
].begin(); ik
!=item
.Fields
[c
].end(); ++ik
)
2215 _Indices
[c
].insert( make_pair( *ik
, _Items
.size()-1 ) );
2221 * Update a row (found by the first column, which must have exactly one element).
2222 * Returns true if it existed before, false if it's being created.
2223 * If it existed before:
2224 * - Does not remove elements that already exist and are not in the new item
2225 * - Adds the new elements found in the new item at the specified columns, and updates lookup map
2227 bool updateItemAppend( const TSortableItem
& item
, uint32 column
)
2230 return true; // quiet
2232 uint32 nameColumn
= 0;
2233 CLookup::iterator ilk
= _Indices
[nameColumn
].find( item
.Fields
[nameColumn
][0] );
2234 if ( ilk
!= _Indices
[nameColumn
].end() )
2236 uint32
& index
= (*ilk
).second
;
2238 // Update map for the specified column
2239 // and update item column
2240 for ( std::vector
<std::string
>::const_iterator ivs
=item
.Fields
[column
].begin(); ivs
!=item
.Fields
[column
].end(); ++ivs
)
2242 ilk
= _Indices
[column
].find( *ivs
);
2243 if ( (ilk
== _Indices
[column
].end()) || ( getRow( (*ilk
).second
).Fields
[nameColumn
][0] != item
.Fields
[nameColumn
][0]) )
2245 _Indices
[column
].insert( make_pair( *ivs
, index
) );
2246 _Items
[index
].Fields
[column
].push_back( *ivs
);
2260 * Update a row (found by the first column, which must have exactly one element).
2261 * Returns true if it existed before, false if it's being created.
2262 * If it existed before:
2263 * - Does not update lookup maps or item for columns that were already present.
2264 * - Adds entries in lookup maps and updates item for new columns (fields that were empty).
2266 /*bool updateItemAppend( const TSortableItem& item )
2269 return true; // quiet
2271 CLookup::iterator ilk = _Indices[0].find( item.Fields[0][0] );
2272 if ( ilk != _Indices[0].end() )
2274 uint32& index = (*ilk).second;
2276 for ( uint32 c=1; c!=NC; ++c )
2278 // Update maps for previously empty columns
2279 if ( _Items[index].Fields[c].empty() )
2281 for ( std::vector<std::string>::iterator ivs=item.Fields[c].begin(); ivs!=item.Fields[c].end(); ++ivs )
2282 _Indices[c].insert( make_pair( *ivs, index ) );
2285 // Update item column
2286 _Items[index].Fields[c] = item.Fields[c];
2298 /// Find or browse by key
2299 CLookup
& lookup( uint32 column
)
2301 return _Indices
[column
];
2304 /// Browse by adding order
2310 /// Get a row by index
2311 TSortableItem
& getRow( uint32 index
)
2313 return _Items
[index
];
2318 CLookup _Indices
[NC
];
2326 typedef CSortableData
<DtNbCols
> CRMData
;
2327 typedef CRMData::TSortableItem TRMItem
;
2333 class CProducedDocHtml
2338 CProducedDocHtml() : _File(NULL
), _Enabled(false) {}
2341 void open( const std::string
& filename
, const std::string
& title
, bool enableFlag
)
2343 _Enabled
= enableFlag
;
2347 _File
= NLMISC::nlfopen( filename
, "wt" );
2348 fprintf( _File
, ("<html><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n<title>" + title
+ "</title>\n</head><body>\n").c_str() );
2352 void write( const std::string
& htmlCode
)
2357 fprintf( _File
, htmlCode
.c_str() );
2361 void writeln( const std::string
& htmlCode
)
2363 write( htmlCode
+ "\n" );
2367 void writebln( const std::string
& htmlCode
)
2369 write( htmlCode
+ "<br>\n" );
2373 void writepln( const std::string
& htmlCode
)
2375 write( "<p>" + htmlCode
+ "</p>\n" );
2384 fprintf( _File
, "</body></html>\n" );
2398 class CProducedDocCSV
2403 CProducedDocCSV() : _File(NULL
), _Enabled(false) {}
2406 void open( const std::string
& filename
, bool enableFlag
)
2408 _Enabled
= enableFlag
;
2412 _File
= NLMISC::nlfopen( filename
, "wt" );
2416 void write( const std::string
& data
)
2421 fprintf( _File
, data
.c_str() );
2425 void writeln( const std::string
& data
)
2427 write( data
+ "\n" );
2450 class CGenRawMaterial
2455 CGenRawMaterial( const std::string
& sheetName
= std::string() ) : SheetName(sheetName
), ILocation(~0), IFamily(~0), IEcosystem(NbEcosystems
), StatQuality(InvalidStatQuality
), Color(InvalidColor
), StatEnergyAvg(0)
2459 void serial( NLMISC::IStream
& s
)
2461 s
.serial( SheetName
);
2462 s
.serial( (uint32
&)ILocation
);
2463 s
.serial( (uint32
&)IFamily
);
2464 s
.serial( (uint32
&)Group
);
2465 s
.serial( (uint32
&)IEcosystem
);
2466 s
.serial( (uint32
&)StatQuality
);
2467 s
.serial( (sint32
&)Color
);
2468 //s.serial( (uint32&)SapLoadLevel );
2469 //s.serial( (uint32&)Rarity );
2470 s
.serial( (sint32
&)StatEnergyAvg
);
2471 s
.serial( (uint32
&)MaxLevel
);
2472 s
.serialCont( RMProperties
);
2473 s
.serialCont( IPropertyDepths
);
2474 s
.serialCont( RMCraftCharacs
);
2477 /// Computes randomly RMCraftCharacs, IPropertyDepths... Returns false if the RM must NOT be generated.
2478 bool computeCraftCharacs( uint iVariant
, const CSString
& sheetName
);
2481 void writeSheet( CForm
*form
);
2484 void loadSheet( CForm
*form
, const std::string
& sheetName
, bool full
);
2487 void collectStats( TRMItem
& item
, CMainStat
& mainStats
);
2489 /// Return average of energies (including max quality as half of the balance)
2490 /*float getEnergyAvg() const
2492 if ( RMCraftCharacs.empty() )
2497 for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics )
2499 sum += (*ics).ActualEnergy;
2501 //return (sum + (float)MaxQuality / 250.0f) / ((float)RMCraftCharacs.size() + 1);
2502 return (sum / (float)RMCraftCharacs.size()); // now, MaxQuality is not part of the average
2507 float getOriginalityAvg() const
2510 for ( list
<CFaberCharacteristics
>::const_iterator ics
=RMCraftCharacs
.begin(); ics
!=RMCraftCharacs
.end(); ++ics
)
2512 sum
+= (*ics
).ActualOriginality
;
2514 return sum
/ (float)RMCraftCharacs
.size();
2518 float getOriginalityMax() const
2520 float maxOriginality
= 0.0f
;
2521 for ( list
<CFaberCharacteristics
>::const_iterator ics
=RMCraftCharacs
.begin(); ics
!=RMCraftCharacs
.end(); ++ics
)
2523 if ( (*ics
).ActualOriginality
> maxOriginality
)
2524 maxOriginality
= (*ics
).ActualOriginality
;
2526 return maxOriginality
;
2530 void fillPropertiesFromFamily()
2532 vs
& props
= FamSet
[familyStr()].Properties
;
2533 RMProperties
.resize( props
.size() );
2534 for ( vs::iterator ip
=props
.begin(); ip
!=props
.end(); ++ip
)
2536 RMProperties
[ip
-props
.begin()] = getIndexFromString( *ip
, properties
);
2541 bool hasCraftPart( uint craftPartIndex
)
2543 return CraftParts
.isEnabled( craftPartIndex
) && (FamSet
[familyStr()].CompatibleCraftParts
.find( string( 1, (char)'A' + craftPartIndex
).c_str() ) != string::npos
);
2547 /*TCiv getCivSpec( uint craftPartIndex, const TFamInfo& famInfo )
2550 for ( vector<TCiv>::const_iterator ivc=famInfo.Civs.begin(); ivc!=famInfo.Civs.end(); ++ivc )
2552 // Skip those not matching the current rFaberElem
2553 if ( famInfo.getCraftPartForProp( ivc-famInfo.Civs.begin() ).find( string( 1, (char)('A' + craftPartIndex) ).c_str() ) != string::npos )
2555 if ( (civ != NbCiv) && ((*ivc) != civ) )
2557 nlwarning( "Different civ specializations for %s, %s (%s and %s)", familyStr().c_str(), getShortFaberElemString( craftPartIndex ).c_str(), CivNames[civ], CivNames[*ivc] );
2570 TCiv
getCivSpec( TEcosystem iEcosystem
, TStatQuality statQuality
)
2572 if ( (statQuality
<= Fine
) || (iEcosystem
>= NbEcosystems
) )
2575 return EcosystemToCiv
[iEcosystem
];
2579 /*TCiv getMainCivSpec( const TFamInfo& famInfo )
2582 for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics )
2584 TCiv civOfCraftPart = getCivSpec( (*ics).FaberElement, famInfo );
2585 if ( (civ != NbCiv) && (civOfCraftPart != civ) )
2589 civ = civOfCraftPart;
2598 /*const char * getMainEcosystemSpec( const TFamInfo& famInfo )
2600 return CivEcosystemCodes[getMainCivSpec( famInfo )];
2606 /// Index in locations
2609 /// Index in families
2613 CSString
familyStr() const { return families
[IFamily
]; }
2619 CSString
groupStr() const { return Group
==~0 ? "" : groups
[Group
]; }
2621 /// Index in ecosystems
2622 TEcosystem IEcosystem
;
2625 CSString
ecosystemStr() const { return ecosystems
[IEcosystem
]; }
2627 /// From Basic) to Supreme)
2628 TStatQuality StatQuality
;
2630 /// From 'b' (Basic) to 'f' (Supreme)
2631 char levelZoneLChar() const { return 'b' + (char)StatQuality
; }
2634 void setStatQuality( char levelZoneChar
) { StatQuality
= (TStatQuality
)(levelZoneChar
- 'b'); }
2643 CSString
colorStr() const { return colors
[Color
]; }
2646 //uint32 SapLoadLevel;
2651 sint32 StatEnergyAvg
;
2656 /// Indices in properties
2660 CSString
propertyStr( uint32 p
) const { return properties
[RMProperties
[p
]]; }
2666 CSString
propertyDepthStr( uint32 p
) const { return PropertyDepths
[IPropertyDepths
[p
]]; }
2669 CFaberCharacteristics
*getCraftSlot( uint rFaberElem
)
2671 std::list
< CFaberCharacteristics
>::iterator icl
;
2672 for ( icl
=RMCraftCharacs
.begin(); icl
!=RMCraftCharacs
.end(); ++icl
)
2674 if ( (*icl
).FaberElement
== rFaberElem
)
2680 /// Randomly generated characs
2681 std::list
< CFaberCharacteristics
> RMCraftCharacs
;
2688 class COriginalitySorter
2692 typedef std::set
< CGenRawMaterial
* > CRMSet
;
2693 typedef std::multimap
< uint32
, CGenRawMaterial
*, std::greater
<uint32
> > CMultiMapByOriginality
;
2696 COriginalitySorter() : RMByOriginalityByCraftSlot( NbFaberElements
) {}
2699 void pushRM( CGenRawMaterial
*rawMaterial
)
2701 RawMaterials
.insert( rawMaterial
);
2703 //InfoLog->displayRawNL( "Inserting RM" );
2704 std::list
< CFaberCharacteristics
>::const_iterator ilc
;
2705 for ( ilc
=rawMaterial
->RMCraftCharacs
.begin(); ilc
!=rawMaterial
->RMCraftCharacs
.end(); ++ilc
)
2707 //InfoLog->displayRawNL( " %u: %s orig=%u", (*ilc).FaberElement, rawMaterial->SheetName.c_str(), (uint32)((*ilc).ActualOriginality*100.0f) );
2708 RMByOriginalityByCraftSlot
[(*ilc
).FaberElement
].insert( make_pair( (uint32
)((*ilc
).ActualOriginality
*100.0f
), rawMaterial
) );
2713 void popAndDeleteRM( CGenRawMaterial
*rawMaterial
)
2716 RawMaterials
.erase( rawMaterial
);
2720 bool alreadyPopped( CGenRawMaterial
*rawMaterial
) const
2722 return RawMaterials
.find( rawMaterial
) == RawMaterials
.end();
2725 /// fromPos and the returned iterator are the pos internal to the COriginalitySorter RM set
2726 /*CRMSet::const_iterator getFirstRMNotInFamilyListFromPos( const set<uint>& familyList, TStatQuality statQuality, CRMSet::const_iterator fromPos ) const
2728 CRMSet::const_iterator irm;
2729 for ( irm=fromPos; irm!=RawMaterials.end(); ++irm )
2731 if ( ((*irm)->StatQuality == statQuality) &&
2732 (familyList.find( (*irm)->IFamily ) == familyList.end()) )
2735 return RawMaterials.end();
2739 CRMSet::iterator
getRMSetBegin() const { return RawMaterials
.begin(); }
2742 CRMSet::iterator
getRMSetEnd() const { return RawMaterials
.end(); }
2745 void deleteAllRemainingRM()
2747 CRMSet::iterator irm
;
2748 for ( irm
=RawMaterials
.begin(); irm
!=RawMaterials
.end(); ++irm
)
2752 RawMaterials
.clear();
2753 // Does not clear the maps by originality
2756 std::vector
< CMultiMapByOriginality
> RMByOriginalityByCraftSlot
;
2760 CRMSet RawMaterials
;
2764 #define checkColor( c ) nlassert( colors[c] == #c );
2770 void loadDFNs( UFormLoader
*formLoader
)
2772 map
<string
, CDfnFieldInfo
> dfnFields
;
2773 NLMISC::CSmartPtr
<UFormDfn
> formDfn
;
2774 formDfn
= formLoader
->loadFormDfn( (rmSheetType
+ ".dfn").c_str() );
2776 nlerror( "Can't find DFN for %s", rmSheetType
.c_str() );
2777 vector
<string
> craftPartsPaths
;
2778 fillFromDFN( formLoader
, dfnFields
, formDfn
, "", rmSheetType
, "mp.MpParam", craftPartsPaths
);
2781 CraftParts
.getNamesAndPaths( craftPartsPaths
);
2783 formDfn
= formLoader
->loadFormDfn( (crSheetType
+ ".dfn").c_str() );
2785 nlerror( "Can't find DFN for %s", crSheetType
.c_str() );
2786 fillFromDFN( formLoader
, dfnFields
, formDfn
, "", crSheetType
);
2788 // Get lists of predefined values from sitem and creature DFN
2789 families
= dfnFields
["mp.Family"].TypePredefinedLabels
;
2790 familyCodes
= dfnFields
["mp.Family"].TypePredefinedValues
;
2791 groups
= dfnFields
["mp.Group"].TypePredefinedLabels
;
2793 //properties = dfnFields["mp.Material property 1"].TypePredefinedLabels;
2794 //nlverify( removeEntryFromList( properties, "Undefined" ) != ~0 );
2795 properties
.resize( craftPartsPaths
.size() );
2796 for ( uint i
=0; i
!=properties
.size(); ++i
) // now, use properties as item part list
2797 properties
[i
] = string( 1, (char)('A' + i
) );
2799 ecosystems
= dfnFields
["mp.Ecosystem"].TypePredefinedLabels
,
2800 colors
= dfnFields
["mp.MpColor"].TypePredefinedLabels
,
2801 creatures
= dfnFields
["Basics.Race"].TypePredefinedLabels
;
2802 seasons
.push_back( "Winter" );
2803 seasons
.push_back( "Spring" );
2804 seasons
.push_back( "Summer" );
2805 seasons
.push_back( "Autumn" );
2806 //removeEntryFromList( families, "Undefined" );
2807 //removeEntryFromList( familyCodes, "0" );
2808 nlverify( removeEntryFromList( ecosystems
, "unknown" ) != ~0 );
2809 nlverify( removeEntryFromList( ecosystems
, "Goo" ) != ~0 );
2810 nlassert( ecosystems
[0] == "Common" );
2811 nlassert( ecosystems
[1] == "Desert" ); // ensure we match with enum TEcosystem!
2812 nlassert( ecosystems
[2] == "Forest" );
2813 nlassert( ecosystems
[3] == "Lacustre" );
2814 nlassert( ecosystems
[4] == "Jungle" );
2815 nlassert( ecosystems
[5] == "PrimeRoots" );
2816 //removeEntryFromList( ecosystems, "Common" ); // TODO
2817 nlassert( NbEcosystems
== ecosystems
.size() );
2818 nlverify( removeEntryFromList( colors
, "None" ) != ~0 );
2819 nlverify( removeEntryFromList( colors
, "UserColor") != ~0 );
2820 nlassert( colors
.size() == NbColors
);
2822 checkColor( Beige
);
2823 checkColor( Green
);
2824 checkColor( Turquoise
);
2826 checkColor( Violet
);
2827 checkColor( White
);
2828 checkColor( Black
);
2830 /*UndefinedProperty = getIndexFromString( "Undefined", properties );
2831 nlassert( UndefinedProperty != ~0 );*/
2836 * Build RMFamilyIndicesByCreatureModel and DepositFamilyIndices
2838 void dispatchFamiliesToLocations()
2840 for ( CFamMap::iterator iss
=FamSet
.begin(); iss
!=FamSet
.end(); ++iss
)
2842 const CSString
& famStr
= (*iss
).first
;
2843 TFamInfo
& famInfo
= (*iss
).second
;
2844 uint iFam
= getIndexFromString( famStr
, families
);
2845 if ( famInfo
.IsInDeposits
)
2848 nlassert( iFam
!= ~0 );
2849 DepositFamilyIndices
.push_back( iFam
);
2851 if ( famInfo
.IsInCreatures
)
2853 // Extract creature name from left of family name (ASSUMES there's no blank in creature name)
2854 CSString creaNameForRMFamily
= famStr
.splitTo( ' ' );
2857 for ( CSkeletonMap::iterator icm
=CreatureModels
.begin(); icm
!=CreatureModels
.end(); ++icm
)
2859 const CSString
& creaModel
= (*icm
).first
;
2860 TSkeletonInfo
& modelInfo
= (*icm
).second
;
2862 if ( modelInfo
.Name
== creaNameForRMFamily
)
2864 RMFamilyIndicesByCreatureModel
[creaModel
].push_back( iFam
);
2865 //nlinfo( "+ %s for %s (now %u models registered)", famStr.c_str(), creaModel.c_str(), RMFamilyIndicesByCreatureModel.size() );
2866 modelInfo
.IsUsed
= true; // Name and AbbrevName are set by deliverCreatureModels()
2870 else switch ( famInfo
.SpecialCreatureTag
[0] )
2872 // Goo & invasion/raid creatures
2875 GooCreatureFamilyIndices
.push_back( iFam
);
2876 nldebug( "Family %s selected for goo creatures", famStr
.c_str() );
2881 if ( famInfo
.SpecialCreatureTag
.size() == 1 )
2883 InvasionRaidCreatureFamilyIndices
['*'].push_back( iFam
);
2884 nldebug( "Family %s selected for all invasion creatures", famStr
.c_str() );
2888 for ( uint c
=1; c
!=famInfo
.SpecialCreatureTag
.size(); ++c
)
2890 InvasionRaidCreatureFamilyIndices
[famInfo
.SpecialCreatureTag
[c
]].push_back( iFam
);
2891 nldebug( "Family %s selected for invasion creature of type %c", famStr
.c_str(), famInfo
.SpecialCreatureTag
[c
] );
2902 * Returns the number of models used
2904 uint
checkSkeletons()
2906 uint32 nbSkeUsed
= 0;
2907 for ( CSkeletonMap::const_iterator isc
=CreatureModels
.begin(); isc
!=CreatureModels
.end(); ++isc
)
2909 const string
& skeFilename
= (*isc
).first
;
2910 const TSkeletonInfo
& ske
= (*isc
).second
;
2911 const bool& used
= (*isc
).second
.IsUsed
;
2914 nldebug( "Model %s (%s) %s", skeFilename
.c_str(), ske
.AbbrevName
.c_str(), used
?"used":"NOT USED" );
2918 nlwarning( "Model %s %s", skeFilename
.c_str(), used
?"":"NOT USED" );
2927 void createDirectoryStructure()
2929 // Create the directory structure
2930 if ( WriteSheetsToDisk
)
2932 for ( uint32 i
=0; i
!=ecosystems
.size(); ++i
)
2934 string dirname
= conventionalDirectory( ecosystems
[i
] );
2935 if ( ! CFile::isExists( rawMaterialPath
+ dirname
) )
2937 CFile::createDirectory( rawMaterialPath
+ dirname
);
2941 if ( ! CFile::isDirectory( rawMaterialPath
+ dirname
) )
2943 nlwarning( "%s already existing but not a directory!", (rawMaterialPath
+ dirname
).c_str() );
2947 string dirname
= "_parent";
2948 if ( ! CFile::isExists( rawMaterialPath
+ dirname
) )
2950 CFile::createDirectory( rawMaterialPath
+ dirname
);
2954 if ( ! CFile::isDirectory( rawMaterialPath
+ dirname
) )
2956 nlwarning( "%s already existing but not a directory!", (rawMaterialPath
+ dirname
).c_str() );
2963 #endif // NL_SRG_UTILITIES_H
2965 /* End of srg_utilities.h */