// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef NL_SRG_UTILITIES_H #define NL_SRG_UTILITIES_H // Misc #include <nel/misc/types_nl.h> #include <nel/misc/sstring.h> #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/misc/smart_ptr.h" #include "nel/misc/command.h" #include "nel/misc/path.h" #include <nel/misc/diff_tool.h> #include <nel/misc/random.h> // Georges #include "nel/georges/u_form.h" #include "nel/georges/u_form_elm.h" #include "nel/georges/u_form_dfn.h" #include "nel/georges/u_form_loader.h" #include "nel/georges/u_type.h" // Georges, bypassing interface #include "georges/stdgeorges.h" #include "georges/form.h" // C #include <time.h> #include <conio.h> // stl #include <set> #include <map> #include <hash_map> using namespace NLMISC; using namespace NLGEORGES; using namespace std; typedef sint TFaberInterestLevel; const TFaberInterestLevel NAInterestLevel = -1; const uint32 NbNomenclaturedFaberLevel = 5; // excluding N/A const char *sNomenclaturedInterestLevels [NbNomenclaturedFaberLevel+1] = { "N/A", "Worst", "Bad", "Average", "Good", "Best" }; // from -1 to 5 CRandom RandomGenerator; typedef CVectorSString vs; typedef map <uint32, vector<uint32> > CRulesFilter; typedef map <CSString, vector<uint32>, CUnsensitiveSStringLessPred > CRulesStrFilter; typedef map <CSString, vs, CUnsensitiveSStringLessPred > CRulesStr2Filter; typedef map< CSString, CSString, CUnsensitiveSStringLessPred > mss; typedef vector<uint32> vu; typedef hash_map< string, string, hash<string> > CTitles; // code -> utf8 title // Write sheet files, or display to screen only bool WriteSheetsToDisk = true; // Overriden by command-line argument after -n string ExtractNamesCsv; const uint32 NbFaberElements = 26; const uint32 RM_INDEX_FAMILY_CODE = 1; const uint32 NB_FAMILY_CODE_CHARS = 4; const uint32 RM_INDEX_CREATURE_CODE = 5; const uint32 NB_CREATURE_CODE_CHARS = 5; const uint32 RM_INDEX_ECOSYSTEM_CODE = 8; const uint32 NB_ECOSYSTEM_CODE_CHARS = 1; const uint32 RM_INDEX_LEVELZONE_CODE = 9; const uint32 SIZE_RAW_MATERIAL_SHEET_FILENAME = 18; /*const uint32 RM_INDEX_INTEREST_LEVEL = 6; const uint32 NB_INTEREST_LEVEL_CHARS = 2; const uint32 RM_INDEX_FABERELEMS_CODE = 8; const uint32 NB_FABERELEMS_CODE_CHARS = 6;*/ const uint32 CR_INDEX_ECOSYSTEM_CODE = 3; // in creature code const uint32 CR_INDEX_LEVELZONE_CODE = 4; const uint32 CR_INDEX_LOCAL_LEVEL_CODE = 5; //const uint32 NB_UNIQUE_LEVELS_PER_ZONE = 4; // not counting the bosses, the fifth equals the first one of next const uint32 NB_UNIQUE_LEVELZONES_PER_CONTINENT = 5; const uint32 MAX_NB_LOCAL_LEVELS = 8; //const uint32 NB_ZONE_LEVELS = 5; // TEMP for forest ecosystem //const uint32 NbFaberInterestLevels = (NB_ZONE_LEVELS * NB_UNIQUE_LEVELS_PER_ZONE) + 1; // excluding N/A string inputSheetPath; bool inputSheetPathLoaded = false; map<string, string> inputSheetPathContent; // short filename without ext, full filename with path string TranslationPath; string SystemMPPath; // These vectors have the same indices : by family vs families; vs familyCodes, ecosystemCodes, propertyCodes, creatureCodes; sint MaxFamilyNum = -1; enum TColor { Red, Beige, Green, Turquoise, Blue, Violet, White, Black, NbColors, InvalidColor=NbColors }; // By ecosystem, color, property, rm group, creature, season vs ecosystems, colors, properties, groups, creatures, seasons; mss adjectives; // DtName must be the 1st one enum TDataCol { DtName, DtTitle, DtRMFamily, DtGroup, DtEcosystem, DtLevelZone, DtStatQuality, DtProp, DtCreature, DtCreaTitle, DtCraftSlotName, DtCraftCivSpec, DtColor, DtAverageEnergy, DtMaxLevel, DtJewelProtectionType, DtCustomizedProperties, DtNbCols }; 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" }; const uint32 NbPropertyDepths = 5; const char *PropertyDepths [NbPropertyDepths] = { "Unknown", "Slightly", "Moderately", "Quite", "Extremely" }; typedef uint32 TGroup; // index in groups, and value in groups .typ //enum TLocation { Under, Over, Flora, Creatures, NB_LOCATIONS, NB_DEPOSITS=Flora+1 }; enum TLocation { InDeposits, InCreatures, NB_LOCATIONS }; vu DepositFamilyIndices; enum TCiv { Fyros, Matis, Tryker, Zorai, AllCiv, NbCiv }; const char *CivNames [NbCiv] = { "Fyros", "Matis", "Tryker", "Zorai", "All" }; const char *CivEcosystemCodes [NbCiv] = { "D", "F", "L", "J", "X" }; enum TEcosystem { CommonEcosystem, Desert, Forest, Lacustre, Jungle, PrimeRoots, NbEcosystems, Goo=NbEcosystems, Invasion, Raid, Event, N, S, T, U, V, W, X, Y, Z, NbEcosystemsPlusExtensions }; TCiv EcosystemToCiv[NbEcosystems] = { AllCiv, Fyros, Matis, Tryker, Zorai, AllCiv }; enum TStatQuality { Basic, Fine, Choice, Excellent, Supreme, NbStatQualities, InvalidStatQuality=NbStatQualities }; char *StatQualityStr [NbStatQualities+1] = { "Basic", "Fine", "Choice", "Excellent", "Supreme", "N/A" }; TStatQuality CreatureLocalLevelToStatQuality [MAX_NB_LOCAL_LEVELS] = { Basic, // 1 (index 0) Fine, // 2 Basic, // 3 Fine, // 4 Excellent, // 5 (named creatures) InvalidStatQuality, // 6 (mission creatures, their RMs have no craft stats) Supreme, // 7 (bosses) Choice // 8 (mini-bosses) }; enum TIndexOfRemarkableStatIndex { RBestA, RBest=RBestA, RWorstA1, RWorst1=RWorstA1, RWorstA2, RWorst2=RWorstA2, RBestB, RWorstB1, RWorstB2, NB_REMARKABLE_STAT_INDICES }; struct TSkeletonInfo { TSkeletonInfo() : IsUsed(false) {} //vs CreaturesOfSke; CSString Name; CSString AbbrevName; //CSString SkGroup; bool IsUsed; // true if has RM }; typedef map<CSString, TSkeletonInfo, CUnsensitiveSStringLessPred> CSkeletonMap; // By skeleton group, by skeleton, bu creature model CRulesStr2Filter SkgroupToModels; CSkeletonMap CreatureModels; mss CreatureToModel; //vs skeletonGroupColumns; set<CSString, CUnsensitiveSStringLessPred> CreatureMainModelsWithoutRM; // By creature model CRulesStrFilter RMFamilyIndicesByCreatureModel; vu GooCreatureFamilyIndices; typedef map<char, vu> CCreatureTypeToFamilyIndices; // the key is the 2nd char of the creature code (h for herbivore, k for kitin...) CCreatureTypeToFamilyIndices InvasionRaidCreatureFamilyIndices; struct TBool { TBool() : Done(false) {} bool Done; }; typedef map< uint, TBool > CDoneMap; // indexed by levelzone map< string, TBool > IsRMSheetGenerated; // indexed by sheetname string rawMaterialPath, creaturePath, creatureAssignmentPath, depositPath; string dirbase; const string oldRmSheetType = "item"; const string rmSheetType = "sitem"; const string crSheetType = "creature"; const string dpSheetType = "deposit"; //const uint32 NbPropertySlots = 10; // obsolete uint32 UndefinedProperty = ~0; /* * */ struct CIconInfo { CSString IconBackground, Icon, IconOver, IconOver2; }; map< CSString, CIconInfo, CUnsensitiveSStringLessPred > Icons; /* * */ class CMainStat { public: /// CMainStat() : SumNbFaberElemsFilled(0), MaxNbFaberElemsFilled(0), MinNbFaberElemsFilled(~0), NbRMByFaberElem( NbFaberElements, 0 ) {} /// Call it when families etc. are ready void init() { NbRMByFaberElemByFamilyAndCiv.resize( families.size() ); for ( uint32 i=0; i!=families.size(); ++i ) { NbRMByFaberElemByFamilyAndCiv[i].resize( NbCiv ); for ( uint32 c=0; c!=NbCiv; ++c ) NbRMByFaberElemByFamilyAndCiv[i][c].resize( NbFaberElements ); } for ( uint32 iEcosystem=0; iEcosystem!=ecosystems.size(); ++iEcosystem ) { for ( uint32 iLoc=0; iLoc!=NB_LOCATIONS; ++iLoc ) { NbRMHavingProperty[iLoc][iEcosystem].resize( properties.size() ); NbRMByFaberElemByEcosystem[iEcosystem].resize( NbFaberElements ); } } } /// bool updateCraftStatistics( uint32 rFaberElem, uint32 iEcosystem, uint32 iFam, TCiv civ ); /// void updateMainStats( uint32 nbFaberElemsFilled ) { SumNbFaberElemsFilled += nbFaberElemsFilled; if ( nbFaberElemsFilled < MinNbFaberElemsFilled ) MinNbFaberElemsFilled = nbFaberElemsFilled; if ( nbFaberElemsFilled> MaxNbFaberElemsFilled ) MaxNbFaberElemsFilled = nbFaberElemsFilled; } // By property and by ecosystem and location vu NbRMHavingProperty [NB_LOCATIONS][NbEcosystems]; uint32 SumNbFaberElemsFilled; uint32 MaxNbFaberElemsFilled; uint32 MinNbFaberElemsFilled; vector<uint32> NbRMByFaberElem;; vector<uint32> NbRMByFaberElemByEcosystem [NbEcosystems]; vector< vector < vector< uint32 > > > NbRMByFaberElemByFamilyAndCiv; }; /* * From georges2csv */ void loadSheetPath() { if (inputSheetPathLoaded) return; NLMISC::createDebug(); NLMISC::WarningLog->addNegativeFilter( "CPath::insertFileInMap" ); CPath::addSearchPath(inputSheetPath, true, false); // for Georges to work properly vector<string> files; CPath::getPathContent (inputSheetPath, true, false, true, files); uint32 i; for (i=0; i<files.size(); ++i) { string filename = files[i]; string filebase = CFile::getFilenameWithoutExtension(filename); inputSheetPathContent[filebase] = filename; } inputSheetPathLoaded = true; } /* * Get a random value in range [0..nbPossibleValues[ * Precondition: nbPossibleValues < 65536 */ inline uint32 getRandomValue( uint32 nbPossibleValues ) { /*double r = (double) rand(); r/= (double) (RAND_MAX+1); // exclude the ceiling value return (uint32)(r * nbPossibleValues);*/ return RandomGenerator.rand( (uint16)(nbPossibleValues-1) ); } /* * */ struct CDfnFieldInfo { CDfnFieldInfo() {} CDfnFieldInfo( const vector<string>& values, const vector<string>& labels ) : TypePredefinedValues(values), TypePredefinedLabels(labels) {} vector<string> TypePredefinedValues; vector<string> TypePredefinedLabels; }; /* * */ struct TIconMapping { const char *FamilyName; const char *IconFilename; }; /* * */ sint getNomenclaturedInterestLevel( TFaberInterestLevel level, TFaberInterestLevel nbInterestLevels ) { return (level == NAInterestLevel) ? 0 : (level * NbNomenclaturedFaberLevel / nbInterestLevels) + 1; } /* * */ //struct CFaberCode //{ // char Ch[NB_FABERELEMS_CODE_CHARS]; //}; /* * */ //struct CFaberCombination //{ // CFaberCombination( TFaberInterestLevel firstLevel, const string& code ) : FirstLevel(firstLevel) // { // memcpy( Code.Ch, &code[0], NB_FABERELEMS_CODE_CHARS ); // } // // TFaberInterestLevel FirstLevel; // CFaberCode Code; //}; /* * */ //class CSheetNameRepository //{ //public: // // /// // void resize( uint32 nbEcosystems, uint32 nbFamilies ) // { // _Container.resize( nbEcosystems ); // for ( uint32 i=0; i!=nbEcosystems; ++i ) // { // _Container[i].resize( nbFamilies ); // } // } // // /// // void insert( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, TFaberInterestLevel level, const string& faberCombinationCode ) // { // // nlassert( faberCombinationCode.size() == NB_FABERELEMS_CODE_CHARS ); // _Container[iEcosystem][iFamily][iCreature].push_back( CFaberCombination( level, faberCombinationCode ) ); // } // // /// // void getFaberCombinationCodes( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, vector<CFaberCombination> **codes ) // { // map < uint32, vector< CFaberCombination > >::iterator im; // /*nldebug( "%u %u -> %u cr (searching for %u)", iEcosystem, iFamily, _Container[iEcosystem][iFamily].size(), iCreature ); // for ( im=_Container[iEcosystem][iFamily].begin(); im!=_Container[iEcosystem][iFamily].end(); ++im ) // nldebug( "cr %u -- %u combinations", (*im).first, (*im).second.size() );*/ // im = _Container[iEcosystem][iFamily].find( iCreature ); // if ( im == _Container[iEcosystem][iFamily].end() ) // *codes = NULL; // else // *codes = &((im)->second); // } // //private: // // /// Indexs: iEcosystem, iFamily, rCreatureSpecialization, rFaberCombination // vector< vector < map < uint32, vector< CFaberCombination > > > > _Container; //}; //void CSheetNameRepository::getFaberCombinationCodes( uint32 iEcosystem, uint32 iFamily, uint32 iCreature, vector<CFaberCombination> **codes ) //CSheetNameRepository RawMaterialRepository; /** * Characteristics. * When adding a new characteristic, ADD IT INTO "v3_source_tables.xls!Item Parts v3"/"rm_item_parts.csv" and mark compatible item parts */ enum TFaberCharacteristic { Durability, Weight, SapLoad, DMG, Speed, Range, DodgeModifier, ParryModifier, AdversaryDodgeModifier, AdversaryParryModifier, ProtectionFactor, MaxSlashProtect, MaxBluntProtect, MaxPierceProtect, ECTF, EPF, OACTF, OAPF, HCTP, HPF, DACTF, DAPF, JewelProtection, CraftCivSpec, NbCharacs }; const char *sCharacs [NbCharacs] = { "Durability", "Weight", "SapLoad", "DMG", "Speed", "Range", "DodgeModifier", "ParryModifier", "AdversaryDodgeModifier", "AdversaryParryModifier", "ProtectionFactor", "MaxSlashingProtection", "MaxBluntProtection", "MaxPiercingProtection", "ElementalCastingTimeFactor", "ElementalPowerFactor", "OffensiveAfflictionCastingTimeFactor", "OffensiveAfflictionPowerFactor", "HealCastingTimeFactor", "HealPowerFactor", "DefensiveAfflictionCastingTimeFactor", "DefensiveAfflictionPowerFactor", "JewelProtectionType", "CraftCivSpec" }; //const bool PositiveCharacs [NbCharacs] = { true, false, true, false, true, true }; //const float MinCharacValues [NbCharacs] = { 100, 0.1f, 10, 0.2f, 100, 0 }; //const float MaxCharacValues [NbCharacs] = { 500, 1.5f, 400, 2.0f, 500, 60 }; //const float PeakCharacValues [NbCharacs] = { 2000, 2.5f, 800, 5.0f, 2000, 80 }; bool PositiveCharacs [NbCharacs]; float MinCharacValues [NbCharacs]; float MaxCharacValues [NbCharacs]; float PeakCharacValues [NbCharacs]; vector<bool> CharacSlotFilter [NbCharacs]; // it's a positive filter /* * */ struct TFamInfo { vs Properties; // ex: propForA, propForB, propForC CSString CompatibleCraftParts; // ex: ABC vu CraftPartsByProp; // ex: 0, 1, 2 (indices in ompatibleCraftParts) vector<TCiv> Civs; // ex: Fyros, All, All vu Freqs; // ex: 1, 2, 2 TGroup Group; bool IsActive; // False if not in rm_fam_prop.csv bool IsInDeposits; bool IsInCreatures; CSString SpecialCreatureTag; bool GenerateOnly; bool IsForMission; sint8 RemarkableStatIndex [NB_REMARKABLE_STAT_INDICES]; sint8 ColorIndex; sint8 JewelProtIndex; static uint UseGenerateOnly; // if 0, generate all; otherwise, generate only families that have GenerateOnly set to true /// TFamInfo() : Group(~0), IsInDeposits(false), IsActive(false), IsInCreatures(false), SpecialCreatureTag(), GenerateOnly(false), IsForMission(false), ColorIndex(-1), JewelProtIndex(-1) {} /// uint getCompatibleCraftPart( uint iCompatibleCP ) const { return (uint)(CompatibleCraftParts[iCompatibleCP] - 'A'); } /// whichProp: index in Properties uint getBeginCraftPartForProp( uint whichProp ) const { return CraftPartsByProp[whichProp]; } /// whichProp: index in Properties uint getEndCraftPartForProp( uint whichProp ) const { if ( whichProp == Properties.size()-1 ) return CompatibleCraftParts.size(); else return CraftPartsByProp[whichProp+1]; } /// whichProp: index in Properties CSString getCraftPartForProp( uint whichProp ) const { uint start = getBeginCraftPartForProp( whichProp ); return CompatibleCraftParts.substr( start, getEndCraftPartForProp( whichProp ) - start ); } /** With the returned index, you can get elt in Property, CraftPartsByProp, Civs and Freqs. * Returns ~0 if not found. */ uint getPropIndexByCraftPart( char itemPart ) const { for ( uint i=0; i!=CraftPartsByProp.size(); ++i ) { char itemPartCode [2]; itemPartCode[0] = itemPart; itemPartCode[1] = '\0'; if ( getCraftPartForProp( i ).find( itemPartCode ) != string::npos ) return i; } return ~0; } /// bool existsInEcosystem( TEcosystem iEcosystem, TStatQuality statQuality ) const; /// static bool mustGenerateFamily( uint iFamily ); }; const uint ITEM_PART_JEWEL_GEM = 17; // R /* * */ struct CFaberCharacteristics { /// Default constructor (for reading) CFaberCharacteristics() : FaberElement(~0), ActualEnergy(0.0f), ActualOriginality(0.0f) { for ( uint32 i=0; i!=NbCharacs; ++i ) Values[i] = 0.0f; } /// void serial( NLMISC::IStream& s ) { s.serial( FaberElement ); s.serial( ActualEnergy ); s.serial( ActualOriginality ); for ( uint32 c=0; c!=NbCharacs; ++c ) s.serial( Values[c] ); } /// void initFaberElement( uint32 rFaberElement ) { FaberElement = rFaberElement; } /// Returns false if the RM must NOT be generated. bool randomizeValues( TFaberInterestLevel interestLevel, TFaberInterestLevel nbInterestLevels, uint iVariant, float widthRatio, float peakOccurRatio, float baseBoost, TEcosystem iEcosystem, uint iFreq, uint iFamily ); /// Returns -1 if the RM must NOT be generated, otherwise return the stat average sint32 computeValues( TStatQuality statQuality, const TFamInfo& famInfo, sint remarkableIndicesSetBaseIndex, TEcosystem iEcosystem, uint iFreq, uint iFamily ); /// void calcQualitativeValues(); /// Index of faber element in "MpParam" uint32 FaberElement; /// Values float Values [NbCharacs]; /// Average of the actual interest of the random values between 0 and 1 float ActualEnergy; /// float ActualOriginality; protected: // Returns false if the RM must NOT be generated. //bool randomizeJewelProtection( TStatQuality statQuality, TEcosystem iEcosystem, uint iFreq, uint iFamily ); void computeJewelProtection( TStatQuality statQuality, TEcosystem iEcosystem, uint iFamily ); }; // void CFaberCharacteristics::calcQualitativeValues() { float actualInterests [NbCharacs]; // Calculate the average of ratio between [0,1] float sumActualInterest = 0.0f; uint nbCharacsUsed = 0; for ( uint r=0; r!=NbCharacs; ++r ) { if ( MaxCharacValues[r] == 0 ) continue; if ( ! CharacSlotFilter[r][FaberElement] ) continue; float interestRatio = (Values[r] / PeakCharacValues[r]); // new: Peak is taken as the max => the Energy is in [0..100] if ( ! PositiveCharacs[r] ) interestRatio = 1.0f - interestRatio; // thus, can be negative actualInterests[r] = interestRatio; sumActualInterest += interestRatio; ++nbCharacsUsed; } if ( nbCharacsUsed == 0 ) return; ActualEnergy = (sumActualInterest / (float)nbCharacsUsed); if ( ActualEnergy > 1.0f ) ActualEnergy = 1.0f; // Calculate the standard deviation (SQRT(SUM((Ai-Aavg)�)/N)) float varianceSum = 0.0f; for ( uint r=0; r!=NbCharacs; ++r ) { if ( MaxCharacValues[r] == 0 ) continue; if ( ! CharacSlotFilter[r][FaberElement] ) continue; varianceSum += sqr( actualInterests[r] - ActualEnergy ); } // Don't normalize standard deviation, otherwise low energy materials will be considered more // original (by average) than high energy materials. They wouldn't be comparable. //if ( ActualEnergy != 0.0f ) ActualOriginality = (float)sqrt( (double)(varianceSum / (float)nbCharacsUsed) ); // / ActualEnergy; //else // nlinfo( "Null energy for craft slot %u", FaberElement ); } /* * Fill childrenToGet if rootNameForGetChildren is not null */ void fillFromDFN( UFormLoader *formLoader, map<string, CDfnFieldInfo>& dfnFields, UFormDfn *formDfn, const string& rootName, const string& dfnFilename, const char *rootNameForGetChildren=NULL, vector<string>& childrenToGet=vector<string>() ) { uint32 i; for ( i=0; i!=formDfn->getNumEntry(); ++i ) { string entryName, rootBase; formDfn->getEntryName( i, entryName ); rootBase = rootName.empty() ? "" : (rootName+"."); UFormDfn::TEntryType entryType; bool array; formDfn->getEntryType( i, entryType, array ); switch ( entryType ) { case UFormDfn::EntryVirtualDfn: { CSmartPtr<UFormDfn> subFormDfn = formLoader->loadFormDfn( (entryName + ".dfn").c_str() ); if ( ! subFormDfn ) nlwarning( "Can't load virtual DFN %s", entryName.c_str() ); else fillFromDFN( formLoader, dfnFields, subFormDfn, rootBase + entryName, entryName + ".dfn", rootNameForGetChildren, childrenToGet ); // recurse break; } case UFormDfn::EntryDfn: // .dfn { UFormDfn *subFormDfn; if ( formDfn->getEntryDfn( i, &subFormDfn) ) { string filename; formDfn->getEntryFilename( i, filename ); fillFromDFN( formLoader, dfnFields, subFormDfn, rootBase + entryName, filename, rootNameForGetChildren, childrenToGet ); // recurse } if ( rootNameForGetChildren && (rootName == rootNameForGetChildren) ) { childrenToGet.push_back( rootBase + entryName ); } break; } case UFormDfn::EntryType: // .typ { vector<string> values, labels; UType *subType; if ( formDfn->getEntryType( i, &subType ) ) { uint32 listSize = subType->getNumDefinition(); if ( listSize > 0 ) { string label, value; for ( uint32 j=0; j!=listSize; ++j ) { subType->getDefinition( j, label, value ); if ( (subType->getIncrement() == "1") && (subType->getType() == UType::UnsignedInt || subType->getType() == UType::SignedInt) ) { // Fill blank entry for skipped identifier values (to allow indexing by identifier value) sint num = atoi( value.c_str() ); while ( num - (sint)values.size() > 0 ) { values.push_back( "" ); labels.push_back( "" ); } } values.push_back( value ); labels.push_back( label ); } } } dfnFields.insert( make_pair( rootBase + entryName, CDfnFieldInfo(values, labels) ) ); //nlinfo( "DFN entry: %s (in %s)", (rootBase + entryName).c_str(), dfnFilename.c_str() ); break; } } } } /* * */ CForm *loadTemplateForm( UFormLoader *formLoader, const string& sheetType ) { CForm *form = (CForm*)formLoader->loadForm( (string("_empty.")+sheetType).c_str() ); if ( ! form ) nlerror( "Can't load sheet _empty.%s", sheetType.c_str() ); return form; } /* * */ void eraseCarriageReturns( string& s ) { const char CR = '\n'; string::size_type p = s.find( CR ); while ( (p=s.find( CR )) != string::npos ) s.erase( p, 1 ); } /* * */ string getNomenclatureCode( const string& longName, set<string>& usedCodes, uint32 nbLetters ) { if ( nbLetters > longName.size() ) nlerror( "Wrong nbLetters for %s", longName.c_str() ); // Start with beginning of name string code = strlwr(longName.substr( 0, nbLetters )); uint32 i = nbLetters-1; while ( usedCodes.find( code ) != usedCodes.end() ) { ++i; if ( i < longName.size() ) { // Substitute last code char by a char from the name (except ' ') if ( longName[i] != ' ' ) code[nbLetters-1] = tolower(longName[i]); else continue; } else { // If no char from the name is suitable, increment the last char of the code until suitable char c=1; while ( usedCodes.find( code ) != usedCodes.end() ) { code[nbLetters-1] = tolower(longName[nbLetters-1]) + c; ++c; if ( code[1] > 'z' ) nlerror( "Impossible to make code for %s", longName.c_str() ); } } } strlwr( code ); usedCodes.insert( code ); return code; } /* * Displays mapping if title not null. */ void buildNomenclatureCodes( const char *title, const vector<string>& longNames, vector<string>& codes, uint32 nbLetters ) { set<string> usedCodeSet; uint32 i; for ( i=0; i!=longNames.size(); ++i ) { codes[i] = getNomenclatureCode( longNames[i], usedCodeSet, nbLetters ); if ( title ) nlinfo( "%s %s -> %s", title, longNames[i].c_str(), codes[i].c_str() ); //DebugLog->displayRawNL( "%s", longNames[i].c_str() ); } } /* * Set the size of a family code to NB_FAMILY_CODE_CHARS */ void normalizeFamilyCode( std::string& s ) { if ( s.size() > NB_FAMILY_CODE_CHARS ) { nlerror( "Family codes limited to %u chars (%s)", NB_FAMILY_CODE_CHARS, s.c_str() ); } else { uint p = s.size(); while ( p < NB_FAMILY_CODE_CHARS ) { s = "0" + s; ++p; } } } /* * */ void loadNomenclatureCodes( const char *title, const vector<string>& longNames, vector<string>& codes, const char *filename ) { if ( longNames.empty() ) { nlwarning( "No nomenclature codes to load. %s", title ? title : "" ); return; } codes.resize( longNames.size() ); char lineBuffer[2048]; FILE *rulesFile; const char *SEPARATOR = ";"; vector<string> args; vector<string>::iterator iarg; vector<string>::const_iterator ivs; if ( (rulesFile = fopen( filename, "r" )) == NULL ) { nlwarning( "Can't find file %s", filename ); } else { while ( ! feof(rulesFile) ) { // Get from file fgets( lineBuffer, 2048, rulesFile ); explode( lineBuffer, SEPARATOR, args ); // Get rid of carriage returns! for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { eraseCarriageReturns( *iarg ); } // Read const uint32 MIN_COLS = 6; const uint32 NAME_COL = 4; const uint32 C_COL = 2; const uint32 R_COL = 3; const uint32 NB_CODE_CHARS = 2; 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 { if ( args[NAME_COL].empty() ) continue; ivs = find( longNames.begin(), longNames.end(), args[NAME_COL] ); if ( ivs == longNames.end() ) nlwarning( "Name %s is not in the names array", args[NAME_COL].c_str() ); else { string code = args[C_COL] + args[R_COL]; if ( code.size() < NB_CODE_CHARS ) { nlwarning( "Invalid partial code for %s: %s", (*ivs).c_str(), code.c_str() ); continue; } else if ( code.size() > NB_CODE_CHARS ) { nlinfo( "Compacting code '%s' for %s", code.c_str(), (*ivs).c_str() ); string::size_type p; while ( (p = code.find( ' ' )) != string::npos ) { code.erase( p, 1 ); } } if ( codes[ivs-longNames.begin()].empty() ) { if ( title ) nlinfo( "%s %s -> %s", title, (*ivs).c_str(), code.c_str() ); codes[ivs-longNames.begin()] = code; } else { if ( code != codes[ivs-longNames.begin()] ) nlwarning( "Invalid nomenclature: (%s and %s for %s: ", codes[ivs-longNames.begin()].c_str(), code.c_str(), (*ivs).c_str() ); } } } } for ( ivs=codes.begin(); ivs!=codes.end(); ++ivs ) { if ( (*ivs).empty() ) nlwarning( "No code found for %s", (*(longNames.begin() + (ivs - codes.begin()))).c_str() ); } } } /* * */ inline sint getLastUsedPropertySlot( uint32 *iProperties, sint lastPropertySlot, uint32 undefinedProperty ) { for ( sint r=lastPropertySlot; r>=0; --r ) { if ( iProperties[r] != undefinedProperty ) return r; } return -1; } /* * */ inline bool passNegativeFilter( const vector<uint32>& incompatibilityList, uint32 iValue ) { vector<uint32>::const_iterator ip = find( incompatibilityList.begin(), incompatibilityList.end(), iValue ); return (ip == incompatibilityList.end()); } /* * */ inline bool passPositiveFilter( const vector<uint32>& compatibilityList, uint32 iValue ) { vector<uint32>::const_iterator ip = find( compatibilityList.begin(), compatibilityList.end(), iValue ); return (ip != compatibilityList.end()); } /* * Reject a prop if it is in the incompatibility list of a family */ bool passPropFamilyFilter( const vector<uint32>& iFamilyRelatedProperties, uint32 iProp ) { return passNegativeFilter( iFamilyRelatedProperties, iProp ); } /* * Reject a creature if NOT in the creature list of a family */ /*bool passCreatureFilter( const vector<uint32>& iFamilyRelatedCreatures, uint32 iCreature ) { //nldebug( "%u related creatures, %s", iFamilyRelatedCreatures.size(), passPositiveFilter( iFamilyRelatedCreatures, iCreature ) ? "TRUE": "FALSE" ); return passPositiveFilter( iFamilyRelatedCreatures, iCreature ); }*/ /* * */ class CStrIComparator : public binary_function<string, string, bool> { public: bool operator() ( const string& s1, const string& s2 ) const { return (nlstricmp( s1, s2 ) == 0); } }; /* * */ void displayList( const vector<string>& v, CLog *log=DebugLog ) { vector<string>::const_iterator ist; for ( ist=v.begin(); ist!=v.end(); ++ist ) log->displayRaw( "%s ", (*ist).c_str() ); log->displayRawNL( "" ); } /* * */ uint32 getIndexFromString( const string& s, const vector<string>& v, bool displayWarning=true ) { if ( v.empty() ) { if ( displayWarning ) nlwarning( "Can't find '%s' in empty array", s.c_str() ); return ~0; } else { vector<string>::const_iterator ist = find_if( v.begin(), v.end(), bind2nd(CStrIComparator(), s) ); if ( ist == v.end() ) { if ( displayWarning ) { nlwarning( "Can't find '%s' in:", s.c_str() ); displayList( v, WarningLog ); } return ~0; } else return ist - v.begin(); } } /* * */ uint32 getIndexFromString( const string& s, const char **array, uint arraySize, bool displayWarning=true ) { if ( arraySize == 0 ) { if ( displayWarning ) nlwarning( "Can't find '%s' in empty array", s.c_str() ); return ~0; } else { for ( uint i=0; i!=arraySize; ++i ) { if ( strlwr(string(array[i])) == strlwr(s) ) return i; } if ( displayWarning ) { nlwarning( "Can't find '%s' in:", s.c_str() ); //displayList( v, WarningLog ); } return ~0; } } /* * Returns the index of the erased element, ~0 if not found */ uint32 removeEntryFromList( vector<string>& v, const string& entry ) { vector<string>::iterator ivs; ivs = find( v.begin(), v.end(), entry ); uint32 index; if ( ivs != v.end() ) { index = ivs - v.begin(); v.erase( ivs ); return index; } else return ~0; } /* * */ bool removeEntryFromListByIndex( vector<string>& v, uint32 index ) { if ( index < v.size() ) { v.erase( v.begin() + index ); return true; } else return false; } typedef void (*TMapDeliveryCallback) ( mss& ); typedef void (*TVectorDeliveryCallback) ( vs& ); /* * */ void loadCSVFile( const char *filename, TMapDeliveryCallback deliveryCallback, bool firstColWithoutName=false ) { char lineBuffer[2048]; FILE *file; const char *SEPARATOR = ";"; vector<string> args; vector<string>::iterator iarg; if ( (file = fopen( filename, "r" )) == NULL ) { nlwarning( "Can't find file %s", filename ); } else { // Read first line as header with column names lineBuffer[0] = '\0'; fgets( lineBuffer, 2048, file ); explode( lineBuffer, SEPARATOR, args ); // Store column names (and get rid of carriage returns!) vector < string > columnNames; mss valuesByName; for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { if ( firstColWithoutName && (iarg == args.begin()) ) { *iarg = "<>"; // override column name for the 1st column } eraseCarriageReturns( *iarg ); columnNames.push_back( *iarg ); valuesByName.insert( make_pair( *iarg, string("") ) ); } while ( ! feof(file) ) { // Get from file lineBuffer[0] = '\0'; fgets( lineBuffer, 2048, file ); explode( lineBuffer, SEPARATOR, args ); // Set values (and get rid of carriage returns!) for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { eraseCarriageReturns( *iarg ); valuesByName[columnNames[iarg-args.begin()]] = *iarg; } // Deliver the wanted fields deliveryCallback( valuesByName ); } } } /* * */ void loadCSVFile( const char *filename, TVectorDeliveryCallback deliveryCallback ) { char lineBuffer[2048]; FILE *file; const char *SEPARATOR = ";"; vs args; vs::iterator iarg; if ( (file = fopen( filename, "r" )) == NULL ) { nlwarning( "Can't find file %s", filename ); } else { while ( ! feof(file) ) { // Get from file lineBuffer[0] = '\0'; fgets( lineBuffer, 2048, file ); explode( lineBuffer, SEPARATOR, args ); // Get rid of carriage returns! for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { eraseCarriageReturns( *iarg ); } // Deliver the wanted fields deliveryCallback( args ); } } } /* * */ void loadValueFile( const char *filename, const vector<string>& keyStrings, vector<sint>& contents, sint defaultValue ) { nlassert( keyStrings.size() == contents.size() ); char lineBuffer[2048]; FILE *rulesFile; const char *SEPARATOR = ";"; vector<string> args; vector<string>::iterator iarg; if ( (rulesFile = fopen( filename, "r" )) == NULL ) { nlwarning( "Can't find file %s", filename ); } else { while ( ! feof(rulesFile) ) { // Get from file lineBuffer[0] = '\0'; fgets( lineBuffer, 2048, rulesFile ); explode( lineBuffer, SEPARATOR, args ); // Get rid of carriage returns! for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { eraseCarriageReturns( *iarg ); } // Read if ( (! args.empty()) && (! args[0].empty()) ) // skip blank lines, and lines with blank header { sint value = defaultValue; for ( uint32 a=0; a!=args.size()-1; ++a ) { if ( ! args[a+1].empty() ) // skip blank entries value = atoi( args[a+1].c_str() ); } uint32 index = getIndexFromString( args[0], keyStrings ); if ( index != ~0 ) { contents[index] = value; } } } fclose( rulesFile ); } } /* * */ void loadRulesFile( const char *filename, const vector<string>& keyStrings, const vector<string>& contentStrings, CRulesFilter& filter, const string& matchExtKeyAtFirstColumn=string() ) { char lineBuffer[2048]; FILE *rulesFile; const char *SEPARATOR = ";"; uint32 firstColumn = matchExtKeyAtFirstColumn.empty() ? 0 : 1; vector<string> args; vector<string>::iterator iarg; if ( (rulesFile = fopen( filename, "r" )) == NULL ) { nlwarning( "Can't find file %s", filename ); } else { while ( ! feof(rulesFile) ) { // Get from file lineBuffer[0] = '\0'; fgets( lineBuffer, 2048, rulesFile ); explode( lineBuffer, SEPARATOR, args ); // Get rid of carriage returns! for ( iarg=args.begin(); iarg!=args.end(); ++iarg ) { eraseCarriageReturns( *iarg ); } // Match with ext key string if set if ( (! matchExtKeyAtFirstColumn.empty()) && (args[0]!=matchExtKeyAtFirstColumn) ) continue; // Read if ( (! args.empty()) && (! args[firstColumn].empty()) ) // skip blank lines, and lines with blank header { vector<uint32> contents; for ( uint32 a=firstColumn; a!=args.size()-1; ++a ) { if ( ! args[a+1].empty() ) // skip blank entries contents.push_back( getIndexFromString( args[a+1], contentStrings ) ); } filter.insert( make_pair( getIndexFromString( args[firstColumn], keyStrings ), contents ) ); } } fclose( rulesFile ); } } /* * 1st column: extKeyStrings (corresponding to the filters 'vector'); 2nd: keyStrings */ void loadRulesFileMulti( const char *filename, const vector<string>& extKeyStrings, const vector<string>& keyStrings, const vector<string>& contentStrings, vector<CRulesFilter>& filters ) { filters.resize( extKeyStrings.size() ); for ( uint32 i=0; i!=filters.size(); ++i ) { loadRulesFile( filename, keyStrings, contentStrings, filters[i], extKeyStrings[i] ); /*CRulesFilter::const_iterator irf; nldebug( "%s", extKeyStrings[i].c_str() ); for ( irf=filters[i].begin(); irf!=filters[i].end(); ++irf ) { nldebug( "%s", keyStrings[(*irf).first].c_str() ); vector<uint32>::const_iterator ivi; for ( ivi=(*irf).second.begin(); ivi!=(*irf).second.end(); ++ivi ) { nldebug( "%u", *ivi ); } }*/ } } /* * Clear the form to reuse it (and all contents below node) */ void clearSheet( CForm *form, UFormElm* node ) { ((CFormElm*)node)->clean(); form->clean(); } /* * Saves to disk if bool WriteSheetsToDisk is true */ void flushSheetToDisk( const string& fullFilename, UForm *form ) { if ( WriteSheetsToDisk ) { COFile output( fullFilename ); form->write( output, false ); } } /* * */ string::size_type findCapital( const string& s, string::size_type startPos ) { string::size_type p; for ( p=startPos; p!=s.size(); ++p ) { if ( (s[p] >= 'A') && (s[p] <= 'Z') ) return p; } return string::npos; } /* * Transform "MyString " into "My string" */ void detachValue( string& s ) { if ( s.size() < 2 ) return; string::size_type p; while ( (p = findCapital( s, 1 )) != string::npos ) { s.insert( p, " " ); s[p+1] = tolower( s[p+1] ); } // Rip off any blank at the end if ( s[s.size()-1] == ' ' ) { s.resize( s.size()-1 ); } } /* * */ void getTransposedMap( CRulesFilter& dest, const CRulesFilter& src ) { CRulesFilter::const_iterator im; for ( im=src.begin(); im!=src.end(); ++im ) { vector<uint32>::const_iterator iv; for ( iv=(*im).second.begin(); iv!=(*im).second.end(); ++iv ) { dest[*iv].push_back( (*im).first ); } } } /* * */ string makeFaberElementCode( uint32 iFaberElement, TFaberInterestLevel level, TFaberInterestLevel nbInterestLevels ) { return toString( "%c%d", 'a' + iFaberElement, getNomenclaturedInterestLevel( level, nbInterestLevels ) ); } /* * */ inline bool hasMatchingFaberLevel( TFaberInterestLevel storedLevel, TFaberInterestLevel submittedLevel ) { return storedLevel <= submittedLevel; } /* * */ //void keepOnlyHighestLevel( vector<CFaberCombination*>& codes ) //{ // nlassert( ! codes.empty() ); // sint maxLevel = -1; // uint32 i; // for ( i=0; i!=codes.size(); ++i ) // { // if ( codes[i]->FirstLevel > maxLevel ) // { // maxLevel = codes[i]->FirstLevel; // } // } // vector<CFaberCombination*> remainingCodes; // for ( i=0; i!=codes.size(); ++i ) // { // if ( codes[i]->FirstLevel == maxLevel ) // remainingCodes.push_back( codes[i] ); // } // // //nldebug( "%u codes, highest level = %u with %u occurences", codes.size(), maxLevel, remainingCodes.size() ); // //nlassert( remainingCodes.size() <= codes.size() ); // codes = remainingCodes; // nlassert( ! codes.empty() ); //} /* * */ bool allIncludedIn( const vu& subset, const vu& bigset ) { vu::const_iterator iv; for ( iv=subset.begin(); iv!=subset.end(); ++iv ) { if ( find( bigset.begin(), bigset.end(), *iv ) == bigset.end() ) return false; } return true; } /* * */ void loadConfigFlag( CConfigFile& configFile, const char *varTitle, bool &flag ) { CConfigFile::CVar *var = configFile.getVarPtr( varTitle ); if ( var ) flag = (var->asInt() == 1); } /* * */ string::size_type getCapitalFromPos( const string& s, string::size_type startPos ) { //nldebug( "%s %u", s.c_str(), startPos ); string::size_type p; for ( p=startPos; p<s.size(); ++p ) { if ( (s[p] >= 'A') && (s[p] <= 'Z') ) return p; } return string::npos; } /* * Also used to make system_mp filenames. * Converts "My Identifier" or "MyIdentifier" to "my_identifier" ("My identifier" to "Myidentifier") */ string conventionalDirectory( const string& dirname ) { if ( dirname.empty() ) return ""; string result = dirname; // Remove blanks string::size_type p = 0; while ( (p = result.find( ' ' )) != string::npos ) { result.erase( p, 1 ); } // Convert capitals to underscores result[0] = tolower( result[0] ); p = 1; while ( (p = getCapitalFromPos( result, p )) != string::npos ) { result.insert( p, "_" ); ++p; result[p] = tolower( result[p] ); } return result; } mss UniqueRMNamesAndSheetCodeHead; /* * */ void readRMNames( mss& values ) { string& name = values["basics.name"]; if ( ! name.empty() ) { string radix = values["FILE"].substr( 0, 5 ); UniqueRMNamesAndSheetCodeHead.insert( make_pair( name, radix ) ); } } /* * */ void loadTitles( const string& sourceWords, const string& sourceBase, const string& languageCode, CTitles& dest ) { STRING_MANAGER::TWorksheet worksheet; STRING_MANAGER::loadExcelSheet( TranslationPath + sourceBase + "/" + sourceWords + "_words_" + languageCode + ".txt", worksheet ); uint cp, cn, nbTitles = 0; if ( worksheet.findCol( ucstring(sourceWords + " ID"), cp ) && worksheet.findCol( ucstring("name"), cn ) ) { for ( std::vector<STRING_MANAGER::TWorksheet::TRow>::iterator ip = worksheet.begin(); ip!=worksheet.end(); ++ip ) { if ( ip == worksheet.begin() ) // skip first row continue; STRING_MANAGER::TWorksheet::TRow& row = *ip; dest.insert( make_pair( row[cp].toString(), row[cn].toUtf8() ) ); ++nbTitles; } } else nlwarning( "%s ID or name not found", sourceWords.c_str() ); nlinfo( "Loaded %u %s titles", nbTitles, sourceWords.c_str() ); } /* * */ void extractRawMaterialNames() { loadCSVFile( ExtractNamesCsv.c_str(), readRMNames ); FILE *output = fopen( (CFile::getFilenameWithoutExtension( ExtractNamesCsv ) + "_output.csv").c_str(), "wt" ); fprintf( output, "Code;Name\n" ); for ( mss::const_iterator iun=UniqueRMNamesAndSheetCodeHead.begin(); iun!=UniqueRMNamesAndSheetCodeHead.end(); ++iun ) { const string& codeRadix = (*iun).second; const string& name = (*iun).first; fprintf( output, "%s;%s\n", codeRadix.c_str(), name.c_str() ); } } /* * */ void cleanExteriorWhitespace( vs& line ) { for ( vs::iterator it=line.begin(); it!=line.end(); ++it ) { CSString& s = (*it); string::size_type p; for ( p=0; p!=s.size(); ++p ) { if ( s[p] != ' ' ) break; } if ( (p != 0) && (p != s.size()) ) s = s.substr( p ); for ( p=0; p!=s.size(); ++p ) { if ( s[s.size()-1-p] != ' ' ) break; } if ( (p != 0) && (p != s.size()) ) s = s.rightCrop( p ); } } uint TFamInfo::UseGenerateOnly = 0; // Only used for deposits; for creature, works with the creature sheets found /*bool TFamInfo::existsInEcosystem( TEcosystem iEcosystem, TStatQuality statQuality ) const { switch ( iEcosystem ) { case CommonEcosystem: // The Common rm exists if the rm family has a freq=2 (or 0 but only in Supreme) return ( (find( Freqs.begin(), Freqs.end(), 2 ) != Freqs.end()) || ((find( Freqs.begin(), Freqs.end(), 0 ) != Freqs.end()) && (statQuality == Supreme)) ); // was: find_if ... bind2nd( equals<uint>(), 1 ) break; case PrimeRoots: // Only freq 1 families exist if the PrimeRoots return find( Freqs.begin(), Freqs.end(), 1 ) != Freqs.end(); break; default: // A rm family exists in the ecosystem matching a civ if the corresponding freq is 1 or 3 { uint iCiv = getIndexFromString( ecosystemCodes[iEcosystem], CivEcosystemCodes, NbCiv, false ); vector<TCiv>::const_iterator it = find( Civs.begin(), Civs.end(), (TCiv)iCiv ); if ( it != Civs.end() ) return (Freqs[it-Civs.begin()] == 1) || (Freqs[it-Civs.begin()] == 3); else return false; } } }*/ // Only used for deposits; bool TFamInfo::existsInEcosystem( TEcosystem iEcosystem, TStatQuality statQuality ) const { if ( find( Freqs.begin(), Freqs.end(), 0 ) != Freqs.end() ) { // Freq 0 => only Common/Supreme return (statQuality == Supreme) && (iEcosystem == CommonEcosystem); } else if ( statQuality <= Fine ) { // Basic, Fine => Common return (iEcosystem == CommonEcosystem); } else { // Choice to Supreme => One per ecosystem return (iEcosystem != CommonEcosystem) && (iEcosystem < NbEcosystems); } } /* * */ struct TCraftPartInfo { CSString Name; CSString Path; uint8 PartIndex; bool Enabled; }; /* * */ class CCraftParts { public: /// CCraftParts() : CraftParts( NbFaberElements ) { for ( uint i=0; i!=NbFaberElements; ++i ) { CraftParts[i].PartIndex = i; CraftParts[i].Enabled = false; } } /// void registerPartChars( const CSString& parts ) { for ( string::size_type p=0; p!=parts.size(); ++p ) { uint index = (uint)(parts[p] - 'A'); CraftParts[index].Enabled = true; } } /// bool isEnabled( uint index ) const { return CraftParts[index].Enabled; } /// void getNamesAndPaths( const vector<string>& paths ) { uint i = 0; vector<string>::const_iterator ip; for ( ip=paths.begin(); ip!=paths.end(); ++ip ) { if ( i >= CraftParts.size() ) nlerror( "Mismatch between sitem DFN and constant (nb of craft parts)" ); CraftParts[i].Path = (*ip); string::size_type p = (*ip).rfind( '.' ) + 1; // string::npos+1 gives 0 CraftParts[i].Name = (*ip).substr( p ); nldebug( "%u: %s", ip-paths.begin(), CraftParts[i].Name.c_str() ); ++i; } } /// TCraftPartInfo& operator[] ( uint index ) { return CraftParts[index]; } vector< TCraftPartInfo > CraftParts; }; typedef map<CSString, TFamInfo, CUnsensitiveSStringLessPred > CFamMap; CFamMap FamSet; CCraftParts CraftParts; enum TFamAndPropLine { LFam, LGroup, LCraftParts, LCiv, LFreq, LLoc, LIconMain, LIconBk, LIconOv1, LIconOv2, LIconSpecial, LCraftPlans, LGenerateOnly, LBaseOfRemarkableStatIndices, LColorIndex = LBaseOfRemarkableStatIndices + NB_REMARKABLE_STAT_INDICES, LJewelProtIndex, NbFamAndPropCols }; // static bool TFamInfo::mustGenerateFamily( uint iFamily ) { if ( families[iFamily].empty() ) return false; else if ( ! TFamInfo::UseGenerateOnly ) return true; else { TFamInfo& famInfo = FamSet[families[iFamily]]; return ( famInfo.GenerateOnly ); } } /* * */ CSString getShortFaberElemString( uint rFaberElem ) { string& longString = CraftParts[rFaberElem].Name; return reinterpret_cast<CSString&>(longString.substr( longString.find( "(" ) + 1 )).rightCrop( 1 ); } /* * */ TCiv getCivFromStr( const CSString& civStr ) { if ( civStr.empty() ) return AllCiv; else { for ( uint i=0; i!=NbCiv; ++i ) { if ( civStr == CSString(CivNames[i]) ) return (TCiv)i; } nlwarning( "Unknown civ '%s'", civStr.c_str() ); return AllCiv; } } /* * */ uint getFreqFromStr( const CSString& freqStr ) { uint f = atoi( freqStr.c_str() ); if ( (f < 1) && (f > 5) ) nlwarning( "Unknown freq '%s'", freqStr.c_str() ); return f; } /* * Returns ~0 if s is empty */ TGroup getNewOrExistingGroupFromStr( const CSString& s ) { uint i = getIndexFromString( s, groups, false ); if ( i == ~0 ) { if ( s.empty() ) return ~0; else { i = groups.size(); groups.push_back( s ); nlinfo( "New group: %s (%u)", s.c_str(), i ); } } return i; } /* * */ void deliverFamAndProp( vs& line ) { if ( line.size() < NbFamAndPropCols ) line.resize( NbFamAndPropCols ); cleanExteriorWhitespace( line ); if ( line[LFam].empty() ) { // Load special icons if ( (line.size() >= LIconSpecial+1) && (! line[LIconSpecial].empty()) && (line[LIconMain].empty()) ) { /*if ( line.size() >= LIconMain+1 ) Icons[line[LIconSpecial]].Icon = line[LIconMain];*/ if ( line.size() >= LIconBk+1 ) Icons[line[LIconSpecial]].IconBackground = line[LIconBk]; if ( line.size() >= LIconOv1+1 ) Icons[line[LIconSpecial]].IconOver = line[LIconOv1]; } return; } // Load icons of families if ( line.size() >= LIconMain+1 ) { Icons[line[LFam]].Icon = line[LIconMain]; if ( ! line[LGroup].empty() ) { // For group, set icon of first family of group found! (for forage source knowledge) if ( Icons.find( line[LGroup] ) == Icons.end() ) { Icons[line[LGroup]].Icon = line[LIconMain]; } } } if ( line.size() >= LIconBk+1 ) Icons[line[LFam]].IconBackground = line[LIconBk]; if ( line.size() >= LIconOv1+1 ) Icons[line[LFam]].IconOver = line[LIconOv1]; TFamInfo& famInfo = FamSet[line[LFam]]; famInfo.IsActive = true; /*if ( ! line[LCraftParts].empty() ) { // Store by property (line[LProp]) famInfo.Properties.push_back( line[LProp] ); famInfo.CraftPartsByProp.push_back( famInfo.CompatibleCraftParts.size() ); // beginning of craft parts chars famInfo.CompatibleCraftParts += line[LCraftParts]; CraftParts.registerPartChars( line[LCraftParts] ); famInfo.Civs.push_back( getCivFromStr( line[LCiv] ) ); famInfo.Freqs.push_back( getFreqFromStr( line[LFreq] ) ); famInfo.IsInDeposits = line[LLoc].contains( "D" ); famInfo.IsInCreatures = line[LLoc].contains( "C" ); if ( ! (famInfo.IsInDeposits || famInfo.IsInCreatures) ) nlwarning( "Unknown loc for %s", line[LFam].c_str() ); }*/ for ( string::size_type p=0; p!=line[LCraftParts].size(); ++p ) { // Store by property = craft part (each char of line[LCraftParts]) CSString craftPart = string( 1, line[LCraftParts][p]); famInfo.Properties.push_back( craftPart ); famInfo.CraftPartsByProp.push_back( famInfo.CompatibleCraftParts.size() ); famInfo.CompatibleCraftParts += craftPart; CraftParts.registerPartChars( craftPart ); famInfo.Civs.push_back( getCivFromStr( line[LCiv] ) ); famInfo.Freqs.push_back( getFreqFromStr( line[LFreq] ) ); } if ( line[LCraftParts].empty() ) { famInfo.Freqs.push_back( getFreqFromStr( line[LFreq] ) ); // freq needed for Rarity computation } famInfo.Group = getNewOrExistingGroupFromStr( line[LGroup] ); famInfo.IsInDeposits = line[LLoc].contains( "D" ); famInfo.IsInCreatures = line[LLoc].contains( "C" ); if ( ! (famInfo.IsInDeposits || famInfo.IsInCreatures) ) { famInfo.SpecialCreatureTag = line[LLoc]; if ( (famInfo.SpecialCreatureTag[0] != 'G') && (famInfo.SpecialCreatureTag[0] != 'I') ) nlwarning( "Unknown loc %s for %s", line[LLoc].c_str(), line[LFam].c_str() ); } famInfo.IsForMission = line[LCraftParts].empty(); for ( uint i=0; i!=NB_REMARKABLE_STAT_INDICES; ++i ) { if ( line[LBaseOfRemarkableStatIndices+i].empty() && (! line[LCraftParts].empty()) && (line[LFreq] != "0") ) nlerror( "%s has empty stat index %u", line[LFam].c_str(), i ); famInfo.RemarkableStatIndex[i] = atoi( line[LBaseOfRemarkableStatIndices+i].c_str() ); } if ( ! line[LColorIndex].empty() ) famInfo.ColorIndex = atoi( line[LColorIndex].c_str() ); if ( ! line[LJewelProtIndex].empty() ) famInfo.JewelProtIndex = atoi( line[LJewelProtIndex].c_str() ); bool markedForGeneration = (line[LGenerateOnly] == "X"); if ( (!markedForGeneration) && famInfo.GenerateOnly ) { nlwarning( "Found duplicate family line with different GenerateOnly setting" ); } else { famInfo.GenerateOnly = markedForGeneration; } if ( famInfo.GenerateOnly ) ++TFamInfo::UseGenerateOnly; } typedef map< TGroup, set<uint32> > CGroupMap; /* * */ void loadFamAndProp( const string& filename, bool displayAll ) { loadCSVFile( filename.c_str(), deliverFamAndProp ); if ( displayAll ) { set<CSString, CUnsensitiveSStringLessPred> propSet; CGroupMap groupMap; /// Generate contents of item_mp_family.typ (and fill group map) nlinfo( "item_mp_family.typ:" ); InfoLog->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" ); uint i = 1; for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss ) { const CSString& famStr = (*iss).first; TFamInfo& famInfo = (*iss).second; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i ); // Get info about props and group for ( vs::iterator ip=famInfo.Properties.begin(); ip!=famInfo.Properties.end(); ++ip ) { propSet.insert( *ip ); } groupMap[ famInfo.Group ].insert( i ); // ~0 is for "no group" (creature's RMs only) ++i; } /* /// Generate family-specialized forage search bricks (TODO) nlinfo( "Family-specialized forage search bricks:"); i = 1; for ( CGroupMap::iterator igm=groupMap.begin(); igm!=groupMap.end(); ++igm ) { CSString skill = toString( "SHFM%u", i ); CSString rmgrpBrickCode = toString( "BHFPMB%02u", i ); CSString rmfamBrickFamCode = "BHFPMI" + string( 1, (char)'A' + ((char)(i-1)) ); uint j = 1; for ( set<uint32>:::iterator ifs=(*igm).begin(); ifs!=(*igm).end(); ++ifs ) { // TODO: modifier of modifier CSString brickCode = rmfamBrickFamCode + toString( "%02u", j ); 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) ); ++j; } } /// Generate family-specialized forage search phrases (TODO) nlinfo( "Family-specialized forage search phrases:"); i = 1; for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss ) { const CSString& famStr = (*iss).first; TFamInfo& famInfo = (*iss).second; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i ); } /// Generate family-specialized forage extraction bricks (TODO) nlinfo( "Family-specialized forage extraction bricks:"); i = 1; for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss ) { const CSString& famStr = (*iss).first; TFamInfo& famInfo = (*iss).second; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i ); ++i; } /// Generate family-specialized forage extraction phrases (TODO) nlinfo( "Family-specialized forage extraction phrases:"); i = 1; for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss ) { const CSString& famStr = (*iss).first; TFamInfo& famInfo = (*iss).second; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", famStr.c_str(), i ); ++i; }*/ /// Generate item_mp_property.typ nlinfo( "Item parts as props:" ); InfoLog->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" ); i = 1; for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=propSet.begin(); iss!=propSet.end(); ++iss ) { InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i ); ++i; } /// Generate item_mp_group.typ nlinfo( "Groups:" ); InfoLog->displayRawNL( "<DEFINITION Label=\"Undefined\" Value=\"0\"/>" ); i = 1; for ( CGroupMap::iterator igm=groupMap.begin(); igm!=groupMap.end(); ++igm ) { if ( (*igm).first != ~0 ) { InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (groups[(*igm).first]).c_str(), i ); ++i; } } /* /// Generate group-specialized forage search bricks (TODO) nlinfo( "Group-specialized forage search bricks:"); i = 1; for ( CGroupMap::iterator igm=groupMap.begin(); igm!=groupMap.end(); ++igm ) { CSString skill = toString( "SHFM%u", i ); CSString rmgrpBrickCode = toString( "BHFPMB%02u", i ); ++i; } /// Generate group-specialized forage search phrases nlinfo( "Group-specialized forage search phrases:"); i = 1; for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss ) { if ( (*iss).empty() ) continue; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i ); ++i; } /// Generate group-specialized forage extraction bricks nlinfo( "Group-specialized forage extraction bricks:"); i = 1; for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss ) { if ( (*iss).empty() ) continue; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i ); ++i; } /// Generate group-specialized forage extraction phrases nlinfo( "Group-specialized forage extraction phrases:"); i = 1; for ( set<CSString, CUnsensitiveSStringLessPred>::iterator iss=groupSet.begin(); iss!=groupSet.end(); ++iss ) { if ( (*iss).empty() ) continue; InfoLog->displayRawNL( "<DEFINITION Label=\"%s\" Value=\"%u\"/>", (*iss).c_str(), i ); ++i; }*/ nlinfo( "TODO: Keep old values when adding new entries" ); nlinfo( "Don't forget to regen craft plans and to map localized texts" ); } } /* * Multi-indexed array. * NC is the number of columns. */ template <uint32 NC> class CSortableData { public: /// A row is made of fields, usually 1 per column but there may be more than one (each one is a key) struct TSortableItem { std::vector<std::string> Fields [NC]; /// void push( uint32 column, const std::string& f, bool allowDuplicates=false ) { if ( (allowDuplicates) || (find( Fields[column].begin(), Fields[column].end(), f ) == Fields[column].end()) ) Fields[column].push_back( f ); } /** * Display the item as a row of a HTML table. * If (key!=previousKey) and (name==previousName), the row will not be displayed entirely to save space * * \param keyColumn If not ~0, column used for sorting => this column displays only the field matching the key * \param key The key used for sorting (see keyColumn) * \param previousKey Previous key * \param nameColumn If not ~0, column used for the unique name (column must have exaclty one element) * \param previousName Previous name */ std::string toHTMLRow( uint32 keyColumn=~0, const string& key=string(), const string& previousKey=string(), uint32 nameColumn=~0, const string& previousName=string() ) const { std::string s = "<tr>"; bool lightMode = (nameColumn == ~0) ? false : ((key != previousKey) && (Fields[nameColumn][0] == previousName)); for ( uint32 c=0; c!=NC; ++c ) { s += "<td>"; if ( c == keyColumn ) s += key; // key should be a substr of toString( c ) else { if ( lightMode ) s += "\""; else s += columnToString( c ); } s += "</td>"; } s += "</tr>\n"; return s; } /// std::string toCSVLine( char columnSeparator=',', string internalSeparator=" - ", uint32 keyColumn=~0, const string& key=string(), const string& previousKey=string(), uint32 nameColumn=~0, const string& previousName=string() ) const { std::string s; bool lightMode = (nameColumn == ~0) ? false : ((key != previousKey) && (Fields[nameColumn][0] == previousName)); for ( uint32 c=0; c!=NC; ++c ) { if ( c == keyColumn ) s += key; // key should be a substr of columnToString( c ) else { if ( lightMode ) s += "\""; else s += columnToString( c, internalSeparator ); } s += columnSeparator; } s += "\n"; return s; } /// std::string columnToString( uint32 column, const std::string& internalSeparator=", " ) const { std::string s; std::vector<std::string>::const_iterator ivs; for ( ivs=Fields[column].begin(); ivs!=Fields[column].end(); ++ivs ) { if ( ivs!=Fields[column].begin() ) s += internalSeparator; s += (*ivs); } return s; } }; typedef std::multimap< std::string, uint32 > CLookup; // key to index (not pt because reallocation invalidates pointers) typedef std::vector< TSortableItem > CItems; /// Init void init( bool enabled ) { _Enabled = enabled; } /// Add a row void addItem( const TSortableItem& item ) { if ( ! _Enabled ) return; _Items.push_back( item ); for ( uint32 c=0; c!=NC; ++c ) { for ( std::vector<std::string>::const_iterator ik=item.Fields[c].begin(); ik!=item.Fields[c].end(); ++ik ) { _Indices[c].insert( make_pair( *ik, _Items.size()-1 ) ); } } } /** * Update a row (found by the first column, which must have exactly one element). * Returns true if it existed before, false if it's being created. * If it existed before: * - Does not remove elements that already exist and are not in the new item * - Adds the new elements found in the new item at the specified columns, and updates lookup map */ bool updateItemAppend( const TSortableItem& item, uint32 column ) { if ( ! _Enabled ) return true; // quiet uint32 nameColumn = 0; CLookup::iterator ilk = _Indices[nameColumn].find( item.Fields[nameColumn][0] ); if ( ilk != _Indices[nameColumn].end() ) { uint32& index = (*ilk).second; // Update map for the specified column // and update item column for ( std::vector<std::string>::const_iterator ivs=item.Fields[column].begin(); ivs!=item.Fields[column].end(); ++ivs ) { ilk = _Indices[column].find( *ivs ); if ( (ilk == _Indices[column].end()) || ( getRow( (*ilk).second ).Fields[nameColumn][0] != item.Fields[nameColumn][0]) ) { _Indices[column].insert( make_pair( *ivs, index ) ); _Items[index].Fields[column].push_back( *ivs ); } } return true; } else { addItem( item ); return false; } } /** * Update a row (found by the first column, which must have exactly one element). * Returns true if it existed before, false if it's being created. * If it existed before: * - Does not update lookup maps or item for columns that were already present. * - Adds entries in lookup maps and updates item for new columns (fields that were empty). */ /*bool updateItemAppend( const TSortableItem& item ) { if ( ! _Enabled ) return true; // quiet CLookup::iterator ilk = _Indices[0].find( item.Fields[0][0] ); if ( ilk != _Indices[0].end() ) { uint32& index = (*ilk).second; for ( uint32 c=1; c!=NC; ++c ) { // Update maps for previously empty columns if ( _Items[index].Fields[c].empty() ) { for ( std::vector<std::string>::iterator ivs=item.Fields[c].begin(); ivs!=item.Fields[c].end(); ++ivs ) _Indices[c].insert( make_pair( *ivs, index ) ); } // Update item column _Items[index].Fields[c] = item.Fields[c]; } return true; } else { addItem( item ); return false; } }*/ /// Find or browse by key CLookup& lookup( uint32 column ) { return _Indices[column]; } /// Browse by adding order CItems& items() { return _Items; } /// Get a row by index TSortableItem& getRow( uint32 index ) { return _Items[index]; } private: CLookup _Indices [NC]; CItems _Items; bool _Enabled; }; typedef CSortableData<DtNbCols> CRMData; typedef CRMData::TSortableItem TRMItem; /** * */ class CProducedDocHtml { public: /// CProducedDocHtml() : _File(NULL), _Enabled(false) {} /// void open( const std::string& filename, const std::string& title, bool enableFlag ) { _Enabled = enableFlag; if ( ! _Enabled ) return; _File = fopen( filename.c_str(), "wt" ); 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() ); } /// void write( const std::string& htmlCode ) { if ( ! _Enabled ) return; fprintf( _File, htmlCode.c_str() ); } /// void writeln( const std::string& htmlCode ) { write( htmlCode + "\n" ); } /// void writebln( const std::string& htmlCode ) { write( htmlCode + "<br>\n" ); } /// void writepln( const std::string& htmlCode ) { write( "<p>" + htmlCode + "</p>\n" ); } /// void save() { if ( ! _Enabled ) return; fprintf( _File, "</body></html>\n" ); fclose( _File ); } private: FILE *_File; bool _Enabled; }; /** * */ class CProducedDocCSV { public: /// CProducedDocCSV() : _File(NULL), _Enabled(false) {} /// void open( const std::string& filename, bool enableFlag ) { _Enabled = enableFlag; if ( ! _Enabled ) return; _File = fopen( filename.c_str(), "wt" ); } /// void write( const std::string& data ) { if ( ! _Enabled ) return; fprintf( _File, data.c_str() ); } /// void writeln( const std::string& data ) { write( data + "\n" ); } /// void save() { if ( ! _Enabled ) return; fclose( _File ); } private: FILE *_File; bool _Enabled; }; /** * */ class CGenRawMaterial { public: /// Constructor CGenRawMaterial( const std::string& sheetName = std::string() ) : SheetName(sheetName), ILocation(~0), IFamily(~0), IEcosystem(NbEcosystems), StatQuality(InvalidStatQuality), Color(InvalidColor), StatEnergyAvg(0) {} /// Serial void serial( NLMISC::IStream& s ) { s.serial( SheetName ); s.serial( (uint32&)ILocation ); s.serial( (uint32&)IFamily ); s.serial( (uint32&)Group ); s.serial( (uint32&)IEcosystem ); s.serial( (uint32&)StatQuality ); s.serial( (sint32&)Color ); //s.serial( (uint32&)SapLoadLevel ); //s.serial( (uint32&)Rarity ); s.serial( (sint32&)StatEnergyAvg ); s.serial( (uint32&)MaxLevel ); s.serialCont( RMProperties ); s.serialCont( IPropertyDepths ); s.serialCont( RMCraftCharacs ); } /// Computes randomly RMCraftCharacs, IPropertyDepths... Returns false if the RM must NOT be generated. bool computeCraftCharacs( uint iVariant, const CSString& sheetName ); /// void writeSheet( CForm *form ); /// void loadSheet( CForm *form, const std::string& sheetName, bool full ); /// void collectStats( TRMItem& item, CMainStat& mainStats ); /// Return average of energies (including max quality as half of the balance) /*float getEnergyAvg() const { if ( RMCraftCharacs.empty() ) return 0.0f; else { float sum = 0.0f; for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics ) { sum += (*ics).ActualEnergy; } //return (sum + (float)MaxQuality / 250.0f) / ((float)RMCraftCharacs.size() + 1); return (sum / (float)RMCraftCharacs.size()); // now, MaxQuality is not part of the average } }*/ /// float getOriginalityAvg() const { float sum = 0.0f; for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics ) { sum += (*ics).ActualOriginality; } return sum / (float)RMCraftCharacs.size(); } /// float getOriginalityMax() const { float maxOriginality = 0.0f; for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics ) { if ( (*ics).ActualOriginality > maxOriginality ) maxOriginality = (*ics).ActualOriginality; } return maxOriginality; } /// void fillPropertiesFromFamily() { vs& props = FamSet[familyStr()].Properties; RMProperties.resize( props.size() ); for ( vs::iterator ip=props.begin(); ip!=props.end(); ++ip ) { RMProperties[ip-props.begin()] = getIndexFromString( *ip, properties ); } } /// bool hasCraftPart( uint craftPartIndex ) { return CraftParts.isEnabled( craftPartIndex ) && (FamSet[familyStr()].CompatibleCraftParts.find( string( 1, (char)'A' + craftPartIndex ).c_str() ) != string::npos); } /// /*TCiv getCivSpec( uint craftPartIndex, const TFamInfo& famInfo ) { TCiv civ = NbCiv; for ( vector<TCiv>::const_iterator ivc=famInfo.Civs.begin(); ivc!=famInfo.Civs.end(); ++ivc ) { // Skip those not matching the current rFaberElem if ( famInfo.getCraftPartForProp( ivc-famInfo.Civs.begin() ).find( string( 1, (char)('A' + craftPartIndex) ).c_str() ) != string::npos ) { if ( (civ != NbCiv) && ((*ivc) != civ) ) { nlwarning( "Different civ specializations for %s, %s (%s and %s)", familyStr().c_str(), getShortFaberElemString( craftPartIndex ).c_str(), CivNames[civ], CivNames[*ivc] ); return AllCiv; } civ = (*ivc); } } if ( civ == NbCiv ) return AllCiv; else if ( civ = return civ; }*/ /// TCiv getCivSpec( TEcosystem iEcosystem, TStatQuality statQuality ) { if ( (statQuality <= Fine) || (iEcosystem >= NbEcosystems) ) return AllCiv; else return EcosystemToCiv[iEcosystem]; } /// /*TCiv getMainCivSpec( const TFamInfo& famInfo ) { TCiv civ = NbCiv; for ( list<CFaberCharacteristics>::const_iterator ics=RMCraftCharacs.begin(); ics!=RMCraftCharacs.end(); ++ics ) { TCiv civOfCraftPart = getCivSpec( (*ics).FaberElement, famInfo ); if ( (civ != NbCiv) && (civOfCraftPart != civ) ) { return AllCiv; } civ = civOfCraftPart; } if ( civ == NbCiv ) return AllCiv; else return civ; }*/ /// /*const char * getMainEcosystemSpec( const TFamInfo& famInfo ) { return CivEcosystemCodes[getMainCivSpec( famInfo )]; }*/ /// Code CSString SheetName; /// Index in locations uint32 ILocation; /// Index in families uint32 IFamily; /// CSString familyStr() const { return families[IFamily]; } /// Group number TGroup Group; /// CSString groupStr() const { return Group==~0 ? "" : groups[Group]; } /// Index in ecosystems TEcosystem IEcosystem; /// CSString ecosystemStr() const { return ecosystems[IEcosystem]; } /// From Basic) to Supreme) TStatQuality StatQuality; /// From 'b' (Basic) to 'f' (Supreme) char levelZoneLChar() const { return 'b' + (char)StatQuality; } /// Same void setStatQuality( char levelZoneChar ) { StatQuality = (TStatQuality)(levelZoneChar - 'b'); } /// For creatures uint32 ILevelZone; /// Index in colors TColor Color; /// CSString colorStr() const { return colors[Color]; } /// Sap load level //uint32 SapLoadLevel; /// Rarity //uint32 Rarity; sint32 StatEnergyAvg; /// Max quality uint32 MaxLevel; /// Indices in properties vu RMProperties; /// CSString propertyStr( uint32 p ) const { return properties[RMProperties[p]]; } /// vu IPropertyDepths; /// CSString propertyDepthStr( uint32 p ) const { return PropertyDepths[IPropertyDepths[p]]; } /// CFaberCharacteristics *getCraftSlot( uint rFaberElem ) { std::list< CFaberCharacteristics >::iterator icl; for ( icl=RMCraftCharacs.begin(); icl!=RMCraftCharacs.end(); ++icl ) { if ( (*icl).FaberElement == rFaberElem ) return &(*icl); } return NULL; } /// Randomly generated characs std::list< CFaberCharacteristics > RMCraftCharacs; }; /** * */ class COriginalitySorter { public: typedef std::set< CGenRawMaterial* > CRMSet; typedef std::multimap< uint32, CGenRawMaterial*, std::greater<uint32> > CMultiMapByOriginality; /// COriginalitySorter() : RMByOriginalityByCraftSlot( NbFaberElements ) {} /// void pushRM( CGenRawMaterial *rawMaterial ) { RawMaterials.insert( rawMaterial ); //InfoLog->displayRawNL( "Inserting RM" ); std::list< CFaberCharacteristics >::const_iterator ilc; for ( ilc=rawMaterial->RMCraftCharacs.begin(); ilc!=rawMaterial->RMCraftCharacs.end(); ++ilc ) { //InfoLog->displayRawNL( " %u: %s orig=%u", (*ilc).FaberElement, rawMaterial->SheetName.c_str(), (uint32)((*ilc).ActualOriginality*100.0f) ); RMByOriginalityByCraftSlot[(*ilc).FaberElement].insert( make_pair( (uint32)((*ilc).ActualOriginality*100.0f), rawMaterial ) ); } } /// void popAndDeleteRM( CGenRawMaterial *rawMaterial ) { delete rawMaterial; RawMaterials.erase( rawMaterial ); } /// bool alreadyPopped( CGenRawMaterial *rawMaterial ) const { return RawMaterials.find( rawMaterial ) == RawMaterials.end(); } /// fromPos and the returned iterator are the pos internal to the COriginalitySorter RM set /*CRMSet::const_iterator getFirstRMNotInFamilyListFromPos( const set<uint>& familyList, TStatQuality statQuality, CRMSet::const_iterator fromPos ) const { CRMSet::const_iterator irm; for ( irm=fromPos; irm!=RawMaterials.end(); ++irm ) { if ( ((*irm)->StatQuality == statQuality) && (familyList.find( (*irm)->IFamily ) == familyList.end()) ) return irm; } return RawMaterials.end(); }*/ /// CRMSet::iterator getRMSetBegin() const { return RawMaterials.begin(); } /// CRMSet::iterator getRMSetEnd() const { return RawMaterials.end(); } /// void deleteAllRemainingRM() { CRMSet::iterator irm; for ( irm=RawMaterials.begin(); irm!=RawMaterials.end(); ++irm ) { delete (*irm); } RawMaterials.clear(); // Does not clear the maps by originality } std::vector< CMultiMapByOriginality > RMByOriginalityByCraftSlot; private: CRMSet RawMaterials; }; #define checkColor( c ) nlassert( colors[c] == #c ); /* * */ void loadDFNs( UFormLoader *formLoader ) { map<string, CDfnFieldInfo> dfnFields; NLMISC::CSmartPtr<UFormDfn> formDfn; formDfn = formLoader->loadFormDfn( (rmSheetType + ".dfn").c_str() ); if ( ! formDfn ) nlerror( "Can't find DFN for %s", rmSheetType.c_str() ); vector<string> craftPartsPaths; fillFromDFN( formLoader, dfnFields, formDfn, "", rmSheetType, "mp.MpParam", craftPartsPaths ); // Get craft parts CraftParts.getNamesAndPaths( craftPartsPaths ); formDfn = formLoader->loadFormDfn( (crSheetType + ".dfn").c_str() ); if ( ! formDfn ) nlerror( "Can't find DFN for %s", crSheetType.c_str() ); fillFromDFN( formLoader, dfnFields, formDfn, "", crSheetType ); // Get lists of predefined values from sitem and creature DFN families = dfnFields["mp.Family"].TypePredefinedLabels; familyCodes = dfnFields["mp.Family"].TypePredefinedValues; groups = dfnFields["mp.Group"].TypePredefinedLabels; //properties = dfnFields["mp.Material property 1"].TypePredefinedLabels; //nlverify( removeEntryFromList( properties, "Undefined" ) != ~0 ); properties.resize( craftPartsPaths.size() ); for ( uint i=0; i!=properties.size(); ++i ) // now, use properties as item part list properties[i] = string( 1, (char)('A' + i) ); ecosystems = dfnFields["mp.Ecosystem"].TypePredefinedLabels, colors = dfnFields["mp.MpColor"].TypePredefinedLabels, creatures = dfnFields["Basics.Race"].TypePredefinedLabels; seasons.push_back( "Winter" ); seasons.push_back( "Spring" ); seasons.push_back( "Summer" ); seasons.push_back( "Autumn" ); //removeEntryFromList( families, "Undefined" ); //removeEntryFromList( familyCodes, "0" ); nlverify( removeEntryFromList( ecosystems, "unknown" ) != ~0 ); nlverify( removeEntryFromList( ecosystems, "Goo" ) != ~0 ); nlassert( ecosystems[0] == "Common" ); nlassert( ecosystems[1] == "Desert" ); // ensure we match with enum TEcosystem! nlassert( ecosystems[2] == "Forest" ); nlassert( ecosystems[3] == "Lacustre" ); nlassert( ecosystems[4] == "Jungle" ); nlassert( ecosystems[5] == "PrimeRoots" ); //removeEntryFromList( ecosystems, "Common" ); // TODO nlassert( NbEcosystems == ecosystems.size() ); nlverify( removeEntryFromList( colors, "None" ) != ~0 ); nlverify( removeEntryFromList( colors, "UserColor") != ~0 ); nlassert( colors.size() == NbColors ); checkColor( Red ); checkColor( Beige ); checkColor( Green ); checkColor( Turquoise ); checkColor( Blue ); checkColor( Violet ); checkColor( White ); checkColor( Black ); /*UndefinedProperty = getIndexFromString( "Undefined", properties ); nlassert( UndefinedProperty != ~0 );*/ } /* * Build RMFamilyIndicesByCreatureModel and DepositFamilyIndices */ void dispatchFamiliesToLocations() { for ( CFamMap::iterator iss=FamSet.begin(); iss!=FamSet.end(); ++iss ) { const CSString& famStr = (*iss).first; TFamInfo& famInfo = (*iss).second; uint iFam = getIndexFromString( famStr, families ); if ( famInfo.IsInDeposits ) { // Deposits nlassert( iFam != ~0 ); DepositFamilyIndices.push_back( iFam ); } if ( famInfo.IsInCreatures ) { // Extract creature name from left of family name (ASSUMES there's no blank in creature name) CSString creaNameForRMFamily = famStr.splitTo( ' ' ); // Dispatch for ( CSkeletonMap::iterator icm=CreatureModels.begin(); icm!=CreatureModels.end(); ++icm ) { const CSString& creaModel = (*icm).first; TSkeletonInfo& modelInfo = (*icm).second; if ( modelInfo.Name == creaNameForRMFamily ) { RMFamilyIndicesByCreatureModel[creaModel].push_back( iFam ); //nlinfo( "+ %s for %s (now %u models registered)", famStr.c_str(), creaModel.c_str(), RMFamilyIndicesByCreatureModel.size() ); modelInfo.IsUsed = true; // Name and AbbrevName are set by deliverCreatureModels() } } } else switch ( famInfo.SpecialCreatureTag[0] ) { // Goo & invasion/raid creatures case 'G': { GooCreatureFamilyIndices.push_back( iFam ); nldebug( "Family %s selected for goo creatures", famStr.c_str() ); break; } case 'I': { if ( famInfo.SpecialCreatureTag.size() == 1 ) { InvasionRaidCreatureFamilyIndices['*'].push_back( iFam ); nldebug( "Family %s selected for all invasion creatures", famStr.c_str() ); } else { for ( uint c=1; c!=famInfo.SpecialCreatureTag.size(); ++c ) { InvasionRaidCreatureFamilyIndices[famInfo.SpecialCreatureTag[c]].push_back( iFam ); nldebug( "Family %s selected for invasion creature of type %c", famStr.c_str(), famInfo.SpecialCreatureTag[c] ); } } break; } } } } /* * Returns the number of models used */ uint checkSkeletons() { uint32 nbSkeUsed = 0; for ( CSkeletonMap::const_iterator isc=CreatureModels.begin(); isc!=CreatureModels.end(); ++isc ) { const string& skeFilename = (*isc).first; const TSkeletonInfo& ske = (*isc).second; const bool& used = (*isc).second.IsUsed; if ( used ) { nldebug( "Model %s (%s) %s", skeFilename.c_str(), ske.AbbrevName.c_str(), used?"used":"NOT USED" ); ++nbSkeUsed; } else nlwarning( "Model %s %s", skeFilename.c_str(), used?"":"NOT USED" ); } return nbSkeUsed; } /* * */ void createDirectoryStructure() { // Create the directory structure if ( WriteSheetsToDisk ) { for ( uint32 i=0; i!=ecosystems.size(); ++i ) { string dirname = conventionalDirectory( ecosystems[i] ); if ( ! CFile::isExists( rawMaterialPath + dirname ) ) { CFile::createDirectory( rawMaterialPath + dirname ); } else { if ( ! CFile::isDirectory( rawMaterialPath + dirname ) ) { nlwarning( "%s already existing but not a directory!", (rawMaterialPath + dirname).c_str() ); } } } string dirname = "_parent"; if ( ! CFile::isExists( rawMaterialPath + dirname ) ) { CFile::createDirectory( rawMaterialPath + dirname ); } else { if ( ! CFile::isDirectory( rawMaterialPath + dirname ) ) { nlwarning( "%s already existing but not a directory!", (rawMaterialPath + dirname).c_str() ); } } } } #endif // NL_SRG_UTILITIES_H /* End of srg_utilities.h */