diff --git a/client/CMT.cpp b/client/CMT.cpp index 962bccbbf..7a0e10a80 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -262,6 +262,26 @@ int main(int argc, char** argv) preinitDLL(::console, logfile); + // Some basic data validation to produce better error messages in cases of incorrect install + auto testFile = [](std::string filename, std::string message) -> bool + { + if (CResourceHandler::get()->existsResource(ResourceID(filename))) + return true; + + tlog1 << "Error: " << message << " was not found!\n"; + return false; + }; + + if (!testFile("DATA/HELP.TXT", "Heroes III data") && + !testFile("DATA/ZELP.TXT", "In the Wake of Gods data") && + !testFile("MODS/VCMI/MOD.JSON", "VCMI mod") && + !testFile("DATA/StackQueueBgBig.PCX", "VCMI data")) + exit(1); // These are unrecoverable errors + + // these two are optional + some installs have them on CD and not in data directory + testFile("VIDEO/GOOD1A.SMK", "campaign movies"); + testFile("SOUNDS/G1A.WAV", "campaign music"); //technically not a music but voiced intro sounds + settings.init(); conf.init(); tlog0 <<"Loading settings: "< artifactClassMap = boost::assign::map_list_of - ("TREASURE", CArtifact::ART_TREASURE) - ("MINOR", CArtifact::ART_MINOR) - ("MAJOR", CArtifact::ART_MAJOR) - ("RELIC", CArtifact::ART_RELIC) - ("SPECIAL", CArtifact::ART_SPECIAL); - -#define ART_BEARER(x) ( #x, ArtBearer::x ) - const std::map artifactBearerMap = boost::assign::map_list_of ART_BEARER_LIST; -#undef ART_BEARER - -#define ART_POS(x) ( #x, ArtifactPosition::x ) - -const std::map artifactPositionMap = boost::assign::map_list_of - ART_POS(HEAD) - ART_POS(SHOULDERS) - ART_POS(NECK) - ART_POS(RIGHT_HAND) - ART_POS(LEFT_HAND) - ART_POS(TORSO) - ART_POS(RIGHT_RING) - ART_POS(LEFT_RING) - ART_POS(FEET) - ART_POS(MISC1) - ART_POS(MISC2) - ART_POS(MISC3) - ART_POS(MISC4) - ART_POS(MISC5) - ART_POS(MACH1) - ART_POS(MACH2) - ART_POS(MACH3) - ART_POS(MACH4) - ART_POS(SPELLBOOK); //no need to specify commander / stack position? +// Note: list must match entries in ArtTraits.txt +#define ART_POS_LIST \ + ART_POS(SPELLBOOK) \ + ART_POS(MACH4) \ + ART_POS(MACH3) \ + ART_POS(MACH2) \ + ART_POS(MACH1) \ + ART_POS(MISC5) \ + ART_POS(MISC4) \ + ART_POS(MISC3) \ + ART_POS(MISC2) \ + ART_POS(MISC1) \ + ART_POS(FEET) \ + ART_POS(LEFT_RING) \ + ART_POS(RIGHT_RING) \ + ART_POS(TORSO) \ + ART_POS(LEFT_HAND) \ + ART_POS(RIGHT_HAND) \ + ART_POS(NECK) \ + ART_POS(SHOULDERS) \ + ART_POS(HEAD); const std::string & CArtifact::Name() const { @@ -166,15 +153,12 @@ void CArtHandler::load(bool onlyTxt) if (onlyTxt) return; // looks to be broken anyway... - std::vector artSlots; - artSlots += 17, 16, 15, 14, 13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0; + #define ART_POS(x) ( #x) + const std::vector artSlots = boost::assign::list_of ART_POS_LIST; + #undef ART_POS - growingArtifacts += ArtifactID::AXE_OF_SMASHING, ArtifactID::MITHRIL_MAIL, - ArtifactID::SWORD_OF_SHARPNESS, ArtifactID::PENDANT_OF_SORCERY, ArtifactID::BOOTS_OF_HASTE, - ArtifactID::BOW_OF_SEEKING, ArtifactID::DRAGON_EYE_RING; - - static std::map classes = - map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC); + static std::map classes = + map_list_of('S',"SPECIAL")('T',"TREASURE")('N',"MINOR")('J',"MAJOR")('R',"RELIC"); CLegacyConfigParser parser("DATA/ARTRAITS.TXT"); CLegacyConfigParser events("DATA/ARTEVENT.TXT"); @@ -184,74 +168,54 @@ void CArtHandler::load(bool onlyTxt) std::map::iterator itr; - for (ArtifactID i=ArtifactID(0); iid=i; - art->iconIndex=i; - art->name = parser.readString(); - art->eventText = events.readString(); - events.endLine(); + std::vector h3Data; - art->price= parser.readNumber(); + for (size_t i = 0; i < GameConstants::ARTIFACTS_QUANTITY; i++) + { + JsonNode artData; + + artData["graphics"]["iconIndex"].Float() = i; + artData["text"]["name"].String() = parser.readString(); + artData["text"]["event"].String() = events.readString(); + artData["value"].Float() = parser.readNumber(); for(int j=0; jpossibleSlots[ArtBearer::HERO].push_back(ArtifactPosition(artSlots[j])); + { + artData["slot"].Vector().push_back(JsonNode()); + artData["slot"].Vector().back().String() = artSlots.at(j); + } } - art->aClass = classes[parser.readString()[0]]; - - //load description and remove quotation marks - art->description = parser.readString(); + artData["class"].String() = classes[parser.readString()[0]]; + artData["text"]["description"].String() = parser.readString(); parser.endLine(); - - if(onlyTxt) // FIXME: pointer to art will be lost. Bug? - continue; - - artifacts.push_back(art); + events.endLine(); + h3Data.push_back(artData); } - if (VLC->modh->modules.COMMANDERS) - { //TODO: move all artifacts config to separate json file - const JsonNode config(ResourceID("config/commanders.json")); - BOOST_FOREACH(const JsonNode &artifact, config["artifacts"].Vector()) - { - auto ga = dynamic_cast (artifacts[artifact["id"].Float()].get()); - BOOST_FOREACH (auto b, artifact["bonusesPerLevel"].Vector()) - { - ga->bonusesPerLevel.push_back (std::pair (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector()))); - } - BOOST_FOREACH (auto b, artifact["thresholdBonuses"].Vector()) - { - ga->thresholdBonuses.push_back (std::pair (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector()))); - } - } - } - - if(onlyTxt) - return; + artifacts.resize(GameConstants::ARTIFACTS_QUANTITY); JsonNode config(ResourceID("config/artifacts.json")); BOOST_FOREACH(auto & node, config["artifacts"].Struct()) { int numeric = node.second["id"].Float(); - CArtifact * art = artifacts[numeric]; + JsonNode & artData = h3Data[numeric]; + JsonUtils::merge(artData, node.second); - loadArtifactJson(art, node.second); + artifacts[numeric] = loadArtifact(artData); + artifacts[numeric]->id = ArtifactID(numeric); VLC->modh->identifiers.registerObject ("artifact." + node.first, numeric); } + + for (size_t i=0; i < artifacts.size(); i++) + { + if (artifacts[i] == nullptr) + tlog0 << "Warning: artifact with id " << i << " is missing!\n"; + } } void CArtHandler::load(std::string objectID, const JsonNode & node) @@ -266,7 +230,16 @@ void CArtHandler::load(std::string objectID, const JsonNode & node) CArtifact * CArtHandler::loadArtifact(const JsonNode & node) { - CArtifact * art = new CArtifact; + CArtifact * art; + + if (!VLC->modh->modules.COMMANDERS || node["growing"].isNull()) + art = new CArtifact(); + else + { + CGrowingArtifact * growing = new CGrowingArtifact(); + loadGrowingArt(growing, node); + art = growing; + } const JsonNode & text = node["text"]; art->name = text["name"].String(); @@ -283,59 +256,90 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node) art->advMapDef = graphics["map"].String(); art->price = node["value"].Float(); - { - auto it = artifactClassMap.find (node["class"].String()); - if (it != artifactClassMap.end()) - { - art->aClass = it->second; - } - else - { - tlog2 << "Warning! Artifact rarity " << node["class"].String() << " not recognized!"; - art->aClass = CArtifact::ART_SPECIAL; - } - } - if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant? - { - std::string slotName = node["slot"].String(); - if (slotName == "MISC") - { - //unfortunatelly slot ids aare not continuous - art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5; - } - else if (slotName == "RING") - { - art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING; - } - else - { + loadSlots(art, node); + loadClass(art, node); + loadType(art, node); + loadComponents(art, node); - auto it = artifactPositionMap.find (slotName); - if (it != artifactPositionMap.end()) - { - auto slot = it->second; - art->possibleSlots[ArtBearer::HERO].push_back (slot); - } - else - tlog2 << "Warning! Artifact slot " << node["slot"].String() << " not recognized!"; - } - } - - loadArtifactJson(art, node); - - return art; -} - -void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact) -{ - BOOST_FOREACH (auto b, artifact["bonuses"].Vector()) + BOOST_FOREACH (auto b, node["bonuses"].Vector()) { auto bonus = JsonUtils::parseBonus (b); bonus->sid = art->id; art->addNewBonus (bonus); } - BOOST_FOREACH (const JsonNode & b, artifact["type"].Vector()) + return art; +} + +void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) +{ +#define ART_POS(x) ( #x, ArtifactPosition::x ) + static const std::map artifactPositionMap = boost::assign::map_list_of ART_POS_LIST; +#undef ART_POS + + if (slotID == "MISC") + { + art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5; + } + else if (slotID == "RING") + { + art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING; + } + else + { + auto it = artifactPositionMap.find (slotID); + if (it != artifactPositionMap.end()) + { + auto slot = it->second; + art->possibleSlots[ArtBearer::HERO].push_back (slot); + } + else + tlog2 << "Warning! Artifact slot " << slotID << " not recognized!"; + } +} + +void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node) +{ + if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant? + { + if (node["slot"].getType() == JsonNode::DATA_STRING) + addSlot(art, node["slot"].String()); + else + { + BOOST_FOREACH (const JsonNode & slot, node["slot"].Vector()) + addSlot(art, slot.String()); + } + } +} + +void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) +{ + static const std::map artifactClassMap = boost::assign::map_list_of + ("TREASURE", CArtifact::ART_TREASURE) + ("MINOR", CArtifact::ART_MINOR) + ("MAJOR", CArtifact::ART_MAJOR) + ("RELIC", CArtifact::ART_RELIC) + ("SPECIAL", CArtifact::ART_SPECIAL); + + auto it = artifactClassMap.find (node["class"].String()); + if (it != artifactClassMap.end()) + { + art->aClass = it->second; + } + else + { + tlog2 << "Warning! Artifact rarity " << node["class"].String() << " not recognized!"; + art->aClass = CArtifact::ART_SPECIAL; + } +} + +void CArtHandler::loadType(CArtifact * art, const JsonNode & node) +{ +#define ART_BEARER(x) ( #x, ArtBearer::x ) + static const std::map artifactBearerMap = boost::assign::map_list_of ART_BEARER_LIST; +#undef ART_BEARER + + BOOST_FOREACH (const JsonNode & b, node["type"].Vector()) { auto it = artifactBearerMap.find (b.String()); if (it != artifactBearerMap.end()) @@ -356,11 +360,14 @@ void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact) else tlog2 << "Warning! Artifact type " << b.String() << " not recognized!"; } +} - if (!artifact["components"].isNull()) +void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node) +{ + if (!node["components"].isNull()) { art->constituents.reset(new std::vector()); - BOOST_FOREACH (auto component, artifact["components"].Vector()) + BOOST_FOREACH (auto component, node["components"].Vector()) { VLC->modh->identifiers.requestIdentifier("artifact." + component.String(), [=](si32 id) { @@ -373,6 +380,18 @@ void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact) } } +void CArtHandler::loadGrowingArt(CGrowingArtifact * art, const JsonNode & node) +{ + BOOST_FOREACH (auto b, node["growing"]["bonusesPerLevel"].Vector()) + { + art->bonusesPerLevel.push_back (std::pair (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector()))); + } + BOOST_FOREACH (auto b, node["growing"]["thresholdBonuses"].Vector()) + { + art->thresholdBonuses.push_back (std::pair (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector()))); + } +} + ArtifactID CArtHandler::creatureToMachineID(CreatureID id) { int dif = 142; diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 1286bd910..bd6db0337 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -59,8 +59,8 @@ public: bool isBig () const; int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other - std::string nodeName() const OVERRIDE; - void addNewBonus(Bonus *b) OVERRIDE; + std::string nodeName() const override; + void addNewBonus(Bonus *b) override; virtual void levelUpArtifact (CArtifactInstance * art){}; @@ -112,7 +112,7 @@ public: //CArtifactInstance(int aid); - std::string nodeName() const OVERRIDE; + std::string nodeName() const override; void deserializationFix(); void setType(CArtifact *Art); @@ -161,11 +161,11 @@ public: std::vector constituentsInfo; - bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const OVERRIDE; - bool canBeDisassembled() const OVERRIDE; - void putAt(ArtifactLocation al) OVERRIDE; - void removeFrom(ArtifactLocation al) OVERRIDE; - bool isPart(const CArtifactInstance *supposedPart) const OVERRIDE; + bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const override; + bool canBeDisassembled() const override; + void putAt(ArtifactLocation al) override; + void removeFrom(ArtifactLocation al) override; + bool isPart(const CArtifactInstance *supposedPart) const override; void createConstituents(); void addAsConstituent(CArtifactInstance *art, ArtifactPosition slot); @@ -187,6 +187,13 @@ public: class DLL_LINKAGE CArtHandler //handles artifacts { + void addSlot(CArtifact * art, const std::string & slotID); + void loadSlots(CArtifact * art, const JsonNode & node); + void loadClass(CArtifact * art, const JsonNode & node); + void loadType(CArtifact * art, const JsonNode & node); + void loadComponents(CArtifact * art, const JsonNode & node); + void loadGrowingArt(CGrowingArtifact * art, const JsonNode & node); + void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype = -1, Bonus::ValueType valType = Bonus::BASE_NUMBER, shared_ptr limiter = shared_ptr(), int additionalinfo = 0); void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype, shared_ptr propagator, int additionalinfo = 0); void giveArtBonus(ArtifactID aid, Bonus *bonus); @@ -204,8 +211,6 @@ public: /// load one artifact from json config CArtifact * loadArtifact(const JsonNode & node); - void loadArtifactJson(CArtifact * art, const JsonNode & node); - void addBonuses(CArtifact *art, const JsonNode &bonusList); void fillList(std::vector &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 2b199e886..7e17e3528 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -85,49 +85,54 @@ void CHeroClassHandler::load() parser.endLine(); // header parser.endLine(); + std::vector h3Data; + do { - CHeroClass * hc = new CHeroClass; + JsonNode entry; - hc->name = parser.readString(); - hc->aggression = parser.readNumber(); - hc->id = heroClasses.size(); + entry["name"].String() = parser.readString(); - hc->primarySkillInitial = parser.readNumArray(GameConstants::PRIMARY_SKILLS); - hc->primarySkillLowLevel = parser.readNumArray(GameConstants::PRIMARY_SKILLS); - hc->primarySkillHighLevel = parser.readNumArray(GameConstants::PRIMARY_SKILLS); + parser.readNumber(); // unused aggression - hc->secSkillProbability = parser.readNumArray(GameConstants::SKILL_QUANTITY); + for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++) + entry["primarySkills"][PrimarySkill::names[i]].Float() = parser.readNumber(); - for(int dd=0; ddselectionProbability[dd] = parser.readNumber(); - } + for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++) + entry["lowLevelChance"][PrimarySkill::names[i]].Float() = parser.readNumber(); - VLC->modh->identifiers.requestIdentifier("faction." + ETownType::names[heroClasses.size()/2], - [=](si32 faction) - { - hc->faction = faction; - }); + for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++) + entry["highLevelChance"][PrimarySkill::names[i]].Float() = parser.readNumber(); - heroClasses.push_back(hc); - VLC->modh->identifiers.registerObject("heroClass." + GameConstants::HERO_CLASSES_NAMES[hc->id], hc->id); + for (size_t i=0; i < GameConstants::SKILL_QUANTITY; i++) + entry["secondarySkills"][NSecondarySkill::names[i]].Float() = parser.readNumber(); + + for(size_t i = 0; i < GameConstants::F_NUMBER; i++) + entry["tavern"][ETownType::names[i]].Float() = parser.readNumber(); + + h3Data.push_back(entry); } while (parser.endLine() && !parser.isNextEntryEmpty()); - const JsonNode & heroGraphics = JsonNode(ResourceID("config/heroClasses.json")); + JsonNode classConf = JsonNode(ResourceID("config/heroClasses.json")); + heroClasses.resize(GameConstants::F_NUMBER * 2); - for (size_t i=0; iimageBattleFemale = battle["female"].String(); - heroClasses[i]->imageBattleMale = battle["male"].String(); + heroClasses[numeric] = loadClass(classData); + heroClasses[numeric]->id = numeric; - const JsonNode & map = heroGraphics["heroMapAnim"].Vector()[i]; + VLC->modh->identifiers.registerObject ("heroClass." + node.first, numeric); + } - heroClasses[i]->imageMapMale = map.String(); - heroClasses[i]->imageMapFemale = map.String(); + for (size_t i=0; i < heroClasses.size(); i++) + { + if (heroClasses[i] == nullptr) + tlog0 << "Warning: class with id " << i << " is missing!\n"; } } diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index cc1b6b77d..d6f05a020 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -98,7 +98,7 @@ class DLL_LINKAGE CHeroClass public: std::string identifier; std::string name; // translatable - double aggression; + //double aggression; // not used in vcmi. TFaction faction; ui8 id; @@ -119,7 +119,7 @@ public: template void serialize(Handler &h, const int version) { - h & identifier & name & faction & aggression; + h & identifier & name & faction;// & aggression; h & primarySkillInitial & primarySkillLowLevel; h & primarySkillHighLevel & secSkillProbability; h & selectionProbability; diff --git a/lib/StringConstants.h b/lib/StringConstants.h index d070b06c0..107a5b501 100644 --- a/lib/StringConstants.h +++ b/lib/StringConstants.h @@ -26,11 +26,6 @@ namespace GameConstants "wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril" }; - const std::string HERO_CLASSES_NAMES [F_NUMBER * 2] = { - "knight", "cleric", "ranger", "druid", "alchemist", "wizard", - "demoniac", "heretic", "deathknight", "necromancer", "warlock", "overlord", - "barbarian", "battlemage", "beastmaster", "witch", "planeswalker", "elementalist" - }; const std::string PLAYER_COLOR_NAMES [PlayerColor::PLAYER_LIMIT_I] = { "red", "blue", "tan", "green", "orange", "purple", "teal", "pink" };