diff --git a/config/heroes/fortress.json b/config/heroes/fortress.json index ca7f8124a..cda96e852 100644 --- a/config/heroes/fortress.json +++ b/config/heroes/fortress.json @@ -12,6 +12,21 @@ "specialties": [ { "type":10, "val": 1, "subtype": 5, "info": 0 } + ], + "army" : + [ + { + "min" : 10, "max" : 20, + "creature" : "gnoll" + }, + { + "min" : 4, "max" : 7, + "creature" : "basilisk" + }, + { + "min" : 2, "max" : 4, + "creature" : "serpentFly" + } ] }, "drakon": diff --git a/config/heroes/special.json b/config/heroes/special.json index 72675bc5a..c0d927fcc 100644 --- a/config/heroes/special.json +++ b/config/heroes/special.json @@ -170,12 +170,15 @@ "army" : [ { + "min" : 10, "max" : 20, "creature" : "troglodyte" }, { + "min" : 4, "max" : 7, "creature" : "harpy" }, { + "min" : 2, "max" : 3, "creature" : "evilEye" } ] @@ -198,12 +201,15 @@ "army" : [ { + "min" : 10, "max" : 20, "creature" : "goblin" }, { + "min" : 4, "max" : 7, "creature" : "goblinWolfRider" }, { + "min" : 2, "max" : 3, "creature" : "orc" } ] @@ -228,12 +234,15 @@ "army" : [ { + "min" : 10, "max" : 20, "creature" : "imp" }, { + "min" : 4, "max" : 7, "creature" : "hellHound" }, { + "min" : 2, "max" : 3, "creature" : "hellHound" } ] diff --git a/debian/control b/debian/control index 20338e976..60390f6bc 100644 --- a/debian/control +++ b/debian/control @@ -17,3 +17,9 @@ Description: Rewrite of the Heroes of Might and Magic 3 engine . In its current state it already supports maps of any sizes, higher resolutions and extended engine limits. + +Package: vcmi-dbg +Architecture: any +Section: debug +Depends: vcmi (= ${binary:Version}), ${misc:Depends} +Description: Debug symbols for vcmi package diff --git a/debian/rules b/debian/rules index 1f40cbadc..f3b8dfbc6 100755 --- a/debian/rules +++ b/debian/rules @@ -6,3 +6,11 @@ # override disabled by default rpath - we need to find libvcmi.so with it: override_dh_auto_configure: dh_auto_configure -- -DCMAKE_SKIP_RPATH=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBIN_DIR=games +.PHONY: override_dh_strip +override_dh_strip: + dh_strip --dbg-package=vcmi-dbg +override_dh_auto_install: + dh_auto_install --destdir=debian/vcmi +override_dh_installdocs: + dh_installdocs --link-doc=vcmi + diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index e6690de07..0bc4b1195 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2957,8 +2957,8 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname) else ss.heroId = -1; - for(const JsonNode &n : n["heroPrimSkills"].Vector()) - ss.heroPrimSkills.push_back(n.Float()); + for(const JsonNode &entry : n["heroPrimSkills"].Vector()) + ss.heroPrimSkills.push_back(entry.Float()); for(const JsonNode &skillNode : n["heroSecSkills"].Vector()) { diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index a6d5c618c..8ada18355 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -22,7 +22,7 @@ * */ -SecondarySkill CHeroClass::chooseSecSkill(const std::set & possibles) const //picks secondary skill out from given possibilities +SecondarySkill CHeroClass::chooseSecSkill(const std::set & possibles, std::minstd_rand & distr) const //picks secondary skill out from given possibilities { int totalProb = 0; for(auto & possible : possibles) @@ -31,7 +31,7 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set & possi } if (totalProb != 0) // may trigger if set contains only banned skills (0 probability) { - int ran = rand()%totalProb; + int ran = distr()%totalProb; for(auto & possible : possibles) { ran -= secSkillProbability[possible]; diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index d1859f63f..465ac723f 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -121,7 +121,7 @@ public: std::string imageMapFemale; bool isMagicHero() const; - SecondarySkill chooseSecSkill(const std::set & possibles) const; //picks secondary skill out from given possibilities + SecondarySkill chooseSecSkill(const std::set & possibles, std::minstd_rand & distr) const; //picks secondary skill out from given possibilities template void serialize(Handler &h, const int version) { diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 8dca5e285..aab728b1a 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -1016,7 +1016,7 @@ void CGHeroInstance::initObj() if(!type) initHero(); //TODO: set up everything for prison before specialties are configured - skillsInfo.randomSeed = rand(); + skillsInfo.distribution.seed(rand()); skillsInfo.resetMagicSchoolCounter(); skillsInfo.resetWisdomCounter(); @@ -1661,14 +1661,19 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const std::vector CGHeroInstance::levelUpProposedSkills() const { std::vector obligatorySkills; //hero is offered magic school or wisdom if possible + if (!skillsInfo.wisdomCounter) + { + if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM)) + obligatorySkills.push_back(SecondarySkill::WISDOM); + } if (!skillsInfo.magicSchoolCounter) { std::vector ss; ss += SecondarySkill::FIRE_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC, SecondarySkill::EARTH_MAGIC; - auto rng = [=](ui32 val)-> ui32 + auto rng = [=](ui32 val) mutable -> ui32 { - return skillsInfo.randomSeed % val; //must be determined + return skillsInfo.distribution() % val; //must be determined }; std::random_shuffle(ss.begin(), ss.end(), rng); @@ -1681,11 +1686,6 @@ std::vector CGHeroInstance::levelUpProposedSkills() const } } } - if (!skillsInfo.wisdomCounter) - { - if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM)) - obligatorySkills.push_back(SecondarySkill::WISDOM); - } std::vector skills; //picking sec. skills for choice @@ -1696,7 +1696,7 @@ std::vector CGHeroInstance::levelUpProposedSkills() const for(auto & elem : secSkills) { - if(elem.second < 3) + if(elem.second < SecSkillLevel::EXPERT) basicAndAdv.insert(elem.first); else expert.insert(elem.first); @@ -1709,35 +1709,41 @@ std::vector CGHeroInstance::levelUpProposedSkills() const expert.erase (s); } - //first offered skill - if (canLearnSkill() && obligatorySkills.size()) //offer always if possible + //first offered skill: + // 1) give obligatory skill + // 2) give any other new skill + // 3) upgrade existing + if (canLearnSkill() && obligatorySkills.size() > 0) { skills.push_back (obligatorySkills[0]); } + else if(none.size() && canLearnSkill()) //hero have free skill slot + { + skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //new skill + } else if(!basicAndAdv.empty()) { - SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv);//upgrade existing + skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.distribution)); //upgrade existing + } + + //second offered skill: + //1) upgrade existing + //2) give obligatory skill + //3) give any other new skill + if(!basicAndAdv.empty()) + { + SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.distribution);//upgrade existing skills.push_back(s); basicAndAdv.erase(s); } - else if(none.size() && canLearnSkill()) - { - skills.push_back(type->heroClass->chooseSecSkill(none)); //give new skill - none.erase(skills.back()); - } - - //second offered skill - if (canLearnSkill() && obligatorySkills.size() > 1) + else if (canLearnSkill() && obligatorySkills.size() > 1) { skills.push_back (obligatorySkills[1]); } - else if(none.size() && canLearnSkill()) //hero have free skill slot + else if(none.size() && canLearnSkill()) { - skills.push_back(type->heroClass->chooseSecSkill(none)); //new skill - } - else if(!basicAndAdv.empty()) - { - skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv)); //upgrade existing + skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.distribution)); //give new skill + none.erase(skills.back()); } return skills; diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index c9bc0b65a..0ce52e3fc 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -350,7 +350,9 @@ public: struct DLL_LINKAGE SecondarySkillsInfo { - ui32 randomSeed; //skills are determined, initialized at map start + //skills are determined, initialized at map start + //FIXME: remove mutable? + mutable std::minstd_rand distribution; ui8 magicSchoolCounter; ui8 wisdomCounter; @@ -359,7 +361,21 @@ public: template void serialize(Handler &h, const int version) { - h & randomSeed & magicSchoolCounter & wisdomCounter; + h & magicSchoolCounter & wisdomCounter; + if (h.saving) + { + std::ostringstream stream; + stream << distribution; + std::string str = stream.str(); + h & str; + } + else + { + std::string str; + h & str; + std::istringstream stream(str); + stream >> distribution; + } } } skillsInfo; diff --git a/lib/Connection.h b/lib/Connection.h index e7962da76..79f276e32 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "ConstTransitivePtr.h" @@ -248,6 +249,30 @@ struct LoadWrong } }; +template +struct VariantLoaderHelper +{ + Source & source; + std::vector> funcs; + + VariantLoaderHelper(Source & source): + source(source) + { + mpl::for_each(std::ref(*this)); + } + + template + void operator()(Type) + { + funcs.push_back([&]() -> Variant + { + Type obj; + source >> obj; + return Variant(obj); + }); + } +}; + template struct SerializationLevel { @@ -1179,27 +1204,20 @@ public: data.resize(length); this->This()->read((void*)data.c_str(),length); } + template void loadSerializable(boost::variant &data) { + typedef boost::variant TVariant; + + VariantLoaderHelper loader(*this); + si32 which; *this >> which; - if(which == 0) - { - T0 obj; - *this >> obj; - data = obj; - } - else if(which == 1) - { - T1 obj; - *this >> obj; - data = obj; - } - else - assert(0); - //TODO write more if needed, general solution would be much longer + assert(which < loader.funcs.size()); + data = loader.funcs.at(which)(); } + template void loadSerializable(boost::optional & data) { diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 6ad0dc501..8b7855808 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -580,20 +580,6 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow if(vstd::contains(t->forbiddenBuildings, ID)) return EBuildingState::FORBIDDEN; //forbidden - auto buildTest = [&](const BuildingID & id) - { - return t->hasBuilt(id); - }; - - if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN) - return EBuildingState::CANT_BUILD_TODAY; //building limit - - if (!building->requirements.test(buildTest)) - return EBuildingState::PREREQUIRES; - - if (building->upgrade != BuildingID::NONE && !t->hasBuilt(building->upgrade)) - return EBuildingState::MISSING_BASE; - if(ID == BuildingID::CAPITOL) { const PlayerState *ps = getPlayer(t->tempOwner); @@ -612,10 +598,24 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow { const TerrainTile *tile = getTile(t->bestLocation(), false); - if(!tile || tile->terType != ETerrainType::WATER) + if(!tile || tile->terType != ETerrainType::WATER) return EBuildingState::NO_WATER; //lack of water } + auto buildTest = [&](const BuildingID & id) + { + return t->hasBuilt(id); + }; + + if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN) + return EBuildingState::CANT_BUILD_TODAY; //building limit + + if (!building->requirements.test(buildTest)) + return EBuildingState::PREREQUIRES; + + if (building->upgrade != BuildingID::NONE && !t->hasBuilt(building->upgrade)) + return EBuildingState::MISSING_BASE; + //checking resources if(!building->resources.canBeAfforded(getPlayer(t->tempOwner)->resources)) return EBuildingState::NO_RESOURCES; //lack of res diff --git a/lib/JsonDetail.cpp b/lib/JsonDetail.cpp index 6ec86527c..47b1f5fc6 100644 --- a/lib/JsonDetail.cpp +++ b/lib/JsonDetail.cpp @@ -131,15 +131,22 @@ JsonNode JsonParser::parse(std::string fileName) { JsonNode root; - if (!Unicode::isValidString(&input[0], input.size())) - error("Not a valid UTF-8 file", false); + if (input.size() == 0) + { + error("File is empty", false); + } + else + { + if (!Unicode::isValidString(&input[0], input.size())) + error("Not a valid UTF-8 file", false); - extractValue(root); - extractWhitespace(false); + extractValue(root); + extractWhitespace(false); - //Warn if there are any non-whitespace symbols left - if (pos < input.size()) - error("Not all file was parsed!", true); + //Warn if there are any non-whitespace symbols left + if (pos < input.size()) + error("Not all file was parsed!", true); + } if (!errors.empty()) { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b45b2aef0..5b7e8b301 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -205,9 +205,9 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) else if(hlu.skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically { sendAndApply(&hlu); - auto rng = [&]()-> ui32 + auto rng = [&]() mutable -> ui32 { - return hero->skillsInfo.randomSeed; //must be determined + return hero->skillsInfo.distribution(); //must be determined }; levelUpHero(hero, vstd::pickRandomElementOf (hlu.skills, rng)); }