1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Merge pull request #3432 from IvanSavenko/crashfixes

[1.4.3] Crashfixes
This commit is contained in:
Ivan Savenko
2024-01-06 13:05:22 +02:00
committed by GitHub
28 changed files with 178 additions and 129 deletions

View File

@@ -205,7 +205,7 @@ int main(int argc, char * argv[])
logGlobal->info("The log file will be saved to %s", logPath); logGlobal->info("The log file will be saved to %s", logPath);
// Init filesystem and settings // Init filesystem and settings
preinitDLL(::console); preinitDLL(::console, false);
Settings session = settings.write["session"]; Settings session = settings.write["session"];
auto setSettingBool = [](std::string key, std::string arg) { auto setSettingBool = [](std::string key, std::string arg) {

View File

@@ -164,6 +164,7 @@ public:
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;}; bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {}; void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {};
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {}; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
void giveExperience(const CGHeroInstance * hero, TExpType val) override {};
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {}; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {};
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {}; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};

View File

@@ -173,8 +173,10 @@ void MapAudioPlayer::updateMusic()
{ {
if(audioPlaying && playerMakingTurn && currentSelection) if(audioPlaying && playerMakingTurn && currentSelection)
{ {
const auto * terrain = LOCPLINT->cb->getTile(currentSelection->visitablePos())->terType; const auto * tile = LOCPLINT->cb->getTile(currentSelection->visitablePos());
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
if (tile)
CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false);
} }
if(audioPlaying && enemyMakingTurn) if(audioPlaying && enemyMakingTurn)

View File

@@ -69,7 +69,7 @@ int ISelectionScreenInfo::getCurrentDifficulty()
PlayerInfo ISelectionScreenInfo::getPlayerInfo(PlayerColor color) PlayerInfo ISelectionScreenInfo::getPlayerInfo(PlayerColor color)
{ {
return getMapInfo()->mapHeader->players[color.getNum()]; return getMapInfo()->mapHeader->players.at(color.getNum());
} }
CSelectionBase::CSelectionBase(ESelectionScreen type) CSelectionBase::CSelectionBase(ESelectionScreen type)

View File

@@ -76,10 +76,10 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
if (text.find("${val}") != std::string::npos) if (text.find("${val}") != std::string::npos)
boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype)))); boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
if (text.find("${subtype.creature}") != std::string::npos) if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>() != CreatureID::NONE)
boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated()); boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated());
if (text.find("${subtype.spell}") != std::string::npos) if (text.find("${subtype.spell}") != std::string::npos && bonus->subtype.as<SpellID>() != SpellID::NONE)
boost::algorithm::replace_all(text, "${subtype.spell}", bonus->subtype.as<SpellID>().toSpell()->getNameTranslated()); boost::algorithm::replace_all(text, "${subtype.spell}", bonus->subtype.as<SpellID>().toSpell()->getNameTranslated());
return text; return text;

View File

@@ -654,14 +654,21 @@ void CHeroHandler::loadExperience()
expPerLevel.push_back(24320); expPerLevel.push_back(24320);
expPerLevel.push_back(28784); expPerLevel.push_back(28784);
expPerLevel.push_back(34140); expPerLevel.push_back(34140);
while (expPerLevel[expPerLevel.size() - 1] > expPerLevel[expPerLevel.size() - 2])
for (;;)
{ {
auto i = expPerLevel.size() - 1; auto i = expPerLevel.size() - 1;
auto diff = expPerLevel[i] - expPerLevel[i-1]; auto currExp = expPerLevel[i];
diff += diff / 5; auto prevExp = expPerLevel[i-1];
expPerLevel.push_back (expPerLevel[i] + diff); auto prevDiff = currExp - prevExp;
auto nextDiff = prevDiff + prevDiff / 5;
auto maxExp = std::numeric_limits<decltype(currExp)>::max();
if (currExp > maxExp - nextDiff)
break; // overflow point reached
expPerLevel.push_back (currExp + nextDiff);
} }
expPerLevel.pop_back();//last value is broken
} }
/// convert h3-style ID (e.g. Gobin Wolf Rider) to vcmi (e.g. goblinWolfRider) /// convert h3-style ID (e.g. Gobin Wolf Rider) to vcmi (e.g. goblinWolfRider)
@@ -741,12 +748,12 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
registerObject(scope, "hero", name, object->getIndex()); registerObject(scope, "hero", name, object->getIndex());
} }
ui32 CHeroHandler::level (ui64 experience) const ui32 CHeroHandler::level (TExpType experience) const
{ {
return static_cast<ui32>(boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel)); return static_cast<ui32>(boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel));
} }
ui64 CHeroHandler::reqExp (ui32 level) const TExpType CHeroHandler::reqExp (ui32 level) const
{ {
if(!level) if(!level)
return 0; return 0;
@@ -762,6 +769,11 @@ ui64 CHeroHandler::reqExp (ui32 level) const
} }
} }
ui32 CHeroHandler::maxSupportedLevel() const
{
return expPerLevel.size();
}
std::set<HeroTypeID> CHeroHandler::getDefaultAllowed() const std::set<HeroTypeID> CHeroHandler::getDefaultAllowed() const
{ {
std::set<HeroTypeID> result; std::set<HeroTypeID> result;

View File

@@ -176,8 +176,8 @@ protected:
class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero, HeroTypeService> class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero, HeroTypeService>
{ {
/// expPerLEvel[i] is amount of exp needed to reach level i; /// expPerLEvel[i] is amount of exp needed to reach level i;
/// consists of 201 values. Any higher levels require experience larger that ui64 can hold /// consists of 196 values. Any higher levels require experience larger that TExpType can hold
std::vector<ui64> expPerLevel; std::vector<TExpType> expPerLevel;
/// helpers for loading to avoid huge load functions /// helpers for loading to avoid huge load functions
void loadHeroArmy(CHero * hero, const JsonNode & node) const; void loadHeroArmy(CHero * hero, const JsonNode & node) const;
@@ -191,8 +191,9 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
public: public:
CHeroClassHandler classes; CHeroClassHandler classes;
ui32 level(ui64 experience) const; //calculates level corresponding to given experience amount ui32 level(TExpType experience) const; //calculates level corresponding to given experience amount
ui64 reqExp(ui32 level) const; //calculates experience required for given level TExpType reqExp(ui32 level) const; //calculates experience required for given level
ui32 maxSupportedLevel() const;
std::vector<JsonNode> loadLegacyData() override; std::vector<JsonNode> loadLegacyData() override;

View File

@@ -84,6 +84,7 @@ public:
virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0; virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
virtual void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) = 0; virtual void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) = 0;
virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0; virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0;
virtual void giveExperience(const CGHeroInstance * hero, TExpType val) =0;
virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false)=0; virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false)=0;
virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
virtual void showBlockingDialog(BlockingDialog *iw) =0; virtual void showBlockingDialog(BlockingDialog *iw) =0;

View File

@@ -47,14 +47,14 @@ VCMI_LIB_NAMESPACE_BEGIN
LibClasses * VLC = nullptr; LibClasses * VLC = nullptr;
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential, bool extractArchives) DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool extractArchives)
{ {
console = Console; console = Console;
VLC = new LibClasses(); VLC = new LibClasses();
VLC->loadFilesystem(extractArchives); VLC->loadFilesystem(extractArchives);
settings.init("config/settings.json", "vcmi:settings"); settings.init("config/settings.json", "vcmi:settings");
persistentStorage.init("config/persistentStorage.json", ""); persistentStorage.init("config/persistentStorage.json", "");
VLC->loadModFilesystem(onlyEssential); VLC->loadModFilesystem();
} }
@@ -182,12 +182,12 @@ void LibClasses::loadFilesystem(bool extractArchives)
logGlobal->info("\tData loading: %d ms", loadTime.getDiff()); logGlobal->info("\tData loading: %d ms", loadTime.getDiff());
} }
void LibClasses::loadModFilesystem(bool onlyEssential) void LibClasses::loadModFilesystem()
{ {
CStopWatch loadTime; CStopWatch loadTime;
modh = new CModHandler(); modh = new CModHandler();
identifiersHandler = new CIdentifierStorage(); identifiersHandler = new CIdentifierStorage();
modh->loadMods(onlyEssential); modh->loadMods();
logGlobal->info("\tMod handler: %d ms", loadTime.getDiff()); logGlobal->info("\tMod handler: %d ms", loadTime.getDiff());
modh->loadModFilesystems(); modh->loadModFilesystems();

View File

@@ -115,7 +115,7 @@ public:
// basic initialization. should be called before init(). Can also extract original H3 archives // basic initialization. should be called before init(). Can also extract original H3 archives
void loadFilesystem(bool extractArchives); void loadFilesystem(bool extractArchives);
void loadModFilesystem(bool onlyEssential); void loadModFilesystem();
#if SCRIPTING_ENABLED #if SCRIPTING_ENABLED
void scriptsLoaded(); void scriptsLoaded();
@@ -124,7 +124,7 @@ public:
extern DLL_LINKAGE LibClasses * VLC; extern DLL_LINKAGE LibClasses * VLC;
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false, bool extractArchives = false); DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool extractArchives);
DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false); DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);

View File

@@ -292,7 +292,7 @@ const Skill * SecondarySkill::toEntity(const Services * services) const
const CCreature * CreatureIDBase::toCreature() const const CCreature * CreatureIDBase::toCreature() const
{ {
return VLC->creh->objects.at(num); return dynamic_cast<const CCreature *>(toEntity(VLC));
} }
const Creature * CreatureIDBase::toEntity(const Services * services) const const Creature * CreatureIDBase::toEntity(const Services * services) const
@@ -324,12 +324,7 @@ std::string CreatureID::entityType()
const CSpell * SpellIDBase::toSpell() const const CSpell * SpellIDBase::toSpell() const
{ {
if(num < 0 || num >= VLC->spellh->objects.size()) return dynamic_cast<const CSpell*>(toEntity(VLC));
{
logGlobal->error("Unable to get spell of invalid ID %d", static_cast<int>(num));
return nullptr;
}
return VLC->spellh->objects[num];
} }
const spells::Spell * SpellIDBase::toEntity(const Services * services) const const spells::Spell * SpellIDBase::toEntity(const Services * services) const

View File

@@ -177,8 +177,10 @@ void CObjectClassesHandler::loadSubObject(const std::string & scope, const std::
auto object = loadSubObjectFromJson(scope, identifier, entry, obj, index); auto object = loadSubObjectFromJson(scope, identifier, entry, obj, index);
assert(object); assert(object);
assert(obj->objects[index] == nullptr); // ensure that this id was not loaded before if (obj->objects.at(index) != nullptr)
obj->objects[index] = object; throw std::runtime_error("Attempt to load already loaded object:" + identifier);
obj->objects.at(index) = object;
registerObject(scope, obj->getJsonKey(), object->getSubTypeName(), object->subtype); registerObject(scope, obj->getJsonKey(), object->getSubTypeName(), object->subtype);
for(const auto & compatID : entry["compatibilityIdentifiers"].Vector()) for(const auto & compatID : entry["compatibilityIdentifiers"].Vector())
@@ -259,10 +261,16 @@ std::unique_ptr<ObjectClass> CObjectClassesHandler::loadFromJson(const std::stri
{ {
const std::string & subMeta = subData.second["index"].meta; const std::string & subMeta = subData.second["index"].meta;
if ( subMeta != "core") if ( subMeta == "core")
logMod->warn("Object %s:%s.%s - attempt to load object with preset index! This option is reserved for built-in mod", subMeta, name, subData.first ); {
size_t subIndex = subData.second["index"].Integer(); size_t subIndex = subData.second["index"].Integer();
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get(), subIndex); loadSubObject(subData.second.meta, subData.first, subData.second, obj.get(), subIndex);
}
else
{
logMod->error("Object %s:%s.%s - attempt to load object with preset index! This option is reserved for built-in mod", subMeta, name, subData.first );
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get());
}
} }
else else
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get()); loadSubObject(subData.second.meta, subData.first, subData.second, obj.get());
@@ -283,28 +291,28 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{ {
assert(objects[index] == nullptr); // ensure that this id was not loaded before assert(objects.at(index) == nullptr); // ensure that this id was not loaded before
objects[index] = loadFromJson(scope, data, name, index); objects.at(index) = loadFromJson(scope, data, name, index);
VLC->identifiersHandler->registerObject(scope, "object", name, objects[index]->id); VLC->identifiersHandler->registerObject(scope, "object", name, objects.at(index)->id);
} }
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID) void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID)
{ {
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL
assert(objects[ID.getNum()]); assert(objects.at(ID.getNum()));
if ( subID.getNum() >= objects[ID.getNum()]->objects.size()) if ( subID.getNum() >= objects.at(ID.getNum())->objects.size())
objects[ID.getNum()]->objects.resize(subID.getNum()+1); objects.at(ID.getNum())->objects.resize(subID.getNum()+1);
JsonUtils::inherit(config, objects.at(ID.getNum())->base); JsonUtils::inherit(config, objects.at(ID.getNum())->base);
loadSubObject(config.meta, identifier, config, objects[ID.getNum()].get(), subID.getNum()); loadSubObject(config.meta, identifier, config, objects.at(ID.getNum()).get(), subID.getNum());
} }
void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID) void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID)
{ {
assert(objects[ID.getNum()]); assert(objects.at(ID.getNum()));
objects[ID.getNum()]->objects[subID.getNum()] = nullptr; objects.at(ID.getNum())->objects.at(subID.getNum()) = nullptr;
} }
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObjectSubID subtype) const TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObjectSubID subtype) const
@@ -337,11 +345,11 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(const std::string & scop
std::optional<si32> id = VLC->identifiers()->getIdentifier(scope, "object", type); std::optional<si32> id = VLC->identifiers()->getIdentifier(scope, "object", type);
if(id) if(id)
{ {
const auto & object = objects[id.value()]; const auto & object = objects.at(id.value());
std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype); std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
if (subID) if (subID)
return object->objects[subID.value()]; return object->objects.at(subID.value());
} }
std::string errorString = "Failed to find object of type " + type + "::" + subtype; std::string errorString = "Failed to find object of type " + type + "::" + subtype;
@@ -472,8 +480,8 @@ std::string CObjectClassesHandler::getObjectName(MapObjectID type, MapObjectSubI
if (handler && handler->hasNameTextID()) if (handler && handler->hasNameTextID())
return handler->getNameTranslated(); return handler->getNameTranslated();
if (objects[type.getNum()]) if (objects.at(type.getNum()))
return objects[type.getNum()]->getNameTranslated(); return objects.at(type.getNum())->getNameTranslated();
return objects.front()->getNameTranslated(); return objects.front()->getNameTranslated();
} }
@@ -487,7 +495,7 @@ SObjectSounds CObjectClassesHandler::getObjectSounds(MapObjectID type, MapObject
if(type == Obj::PRISON || type == Obj::HERO || type == Obj::SPELL_SCROLL) if(type == Obj::PRISON || type == Obj::HERO || type == Obj::SPELL_SCROLL)
subtype = 0; subtype = 0;
if(objects[type.getNum()]) if(objects.at(type.getNum()))
return getHandlerFor(type, subtype)->getSounds(); return getHandlerFor(type, subtype)->getSounds();
else else
return objects.front()->objects.front()->getSounds(); return objects.front()->objects.front()->getSounds();

View File

@@ -1439,7 +1439,7 @@ void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ui8
bool CGHeroInstance::gainsLevel() const bool CGHeroInstance::gainsLevel() const
{ {
return exp >= static_cast<TExpType>(VLC->heroh->reqExp(level+1)); return level < VLC->heroh->maxSupportedLevel() && exp >= static_cast<TExpType>(VLC->heroh->reqExp(level+1));
} }
void CGHeroInstance::levelUp(const std::vector<SecondarySkill> & skills) void CGHeroInstance::levelUp(const std::vector<SecondarySkill> & skills)

View File

@@ -249,8 +249,12 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
iw.player = cb->getOwner(heroID); iw.player = cb->getOwner(heroID);
iw.text.appendRawString(getVisitingBonusGreeting()); iw.text.appendRawString(getVisitingBonusGreeting());
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
cb->changePrimSkill (cb->getHero(heroID), what, val); if (what == PrimarySkill::EXPERIENCE)
town->addHeroToStructureVisitors(h, indexOnTV); cb->giveExperience(cb->getHero(heroID), val);
else
cb->changePrimSkill(cb->getHero(heroID), what, val);
town->addHeroToStructureVisitors(h, indexOnTV);
} }
} }
} }

View File

@@ -1138,7 +1138,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
xp = h->calculateXp(static_cast<int>(xp)); xp = h->calculateXp(static_cast<int>(xp));
iw.text.appendLocalString(EMetaText::ADVOB_TXT,132); iw.text.appendLocalString(EMetaText::ADVOB_TXT,132);
iw.text.replaceNumber(static_cast<int>(xp)); iw.text.replaceNumber(static_cast<int>(xp));
cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, xp, false); cb->giveExperience(h, xp);
} }
else else
{ {

View File

@@ -237,19 +237,12 @@ void CModHandler::loadOneMod(std::string modName, const std::string & parent, co
} }
} }
void CModHandler::loadMods(bool onlyEssential) void CModHandler::loadMods()
{ {
JsonNode modConfig; JsonNode modConfig;
if(onlyEssential) modConfig = loadModSettings(JsonPath::builtin("config/modSettings.json"));
{ loadMods("", "", modConfig["activeMods"], true);
loadOneMod("vcmi", "", modConfig, true);//only vcmi and submods
}
else
{
modConfig = loadModSettings(JsonPath::builtin("config/modSettings.json"));
loadMods("", "", modConfig["activeMods"], true);
}
coreMod = std::make_unique<CModInfo>(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json"))); coreMod = std::make_unique<CModInfo>(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json")));
} }
@@ -346,20 +339,25 @@ void CModHandler::loadModFilesystems()
TModID CModHandler::findResourceOrigin(const ResourcePath & name) const TModID CModHandler::findResourceOrigin(const ResourcePath & name) const
{ {
for(const auto & modID : boost::adaptors::reverse(activeMods)) try
{ {
if(CResourceHandler::get(modID)->existsResource(name)) for(const auto & modID : boost::adaptors::reverse(activeMods))
return modID; {
if(CResourceHandler::get(modID)->existsResource(name))
return modID;
}
if(CResourceHandler::get("core")->existsResource(name))
return "core";
if(CResourceHandler::get("mapEditor")->existsResource(name))
return "core"; // Workaround for loading maps via map editor
} }
catch( const std::out_of_range & e)
if(CResourceHandler::get("core")->existsResource(name)) {
return "core"; // no-op
}
if(CResourceHandler::get("mapEditor")->existsResource(name)) throw std::runtime_error("Resource with name " + name.getName() + " and type " + EResTypeHelper::getEResTypeAsString(name.getType()) + " wasn't found.");
return "core"; // Workaround for loading maps via map editor
assert(0);
return "";
} }
std::string CModHandler::getModLanguage(const TModID& modId) const std::string CModHandler::getModLanguage(const TModID& modId) const

View File

@@ -58,7 +58,7 @@ public:
/// receives list of available mods and trying to load mod.json from all of them /// receives list of available mods and trying to load mod.json from all of them
void initializeConfig(); void initializeConfig();
void loadMods(bool onlyEssential = false); void loadMods();
void loadModFilesystems(); void loadModFilesystems();
/// returns ID of mod that provides selected file resource /// returns ID of mod that provides selected file resource

View File

@@ -115,11 +115,13 @@ bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
continue; continue;
} }
if (vstd::contains(data.Struct(), "index") && !data["index"].isNull()) bool hasIndex = vstd::contains(data.Struct(), "index") && !data["index"].isNull();
{
if (modName != "core")
logMod->warn("Mod %s is attempting to load original data! This should be reserved for built-in mod.", modName);
if (hasIndex && modName != "core")
logMod->error("Mod %s is attempting to load original data! This option is reserved for built-in mod.", modName);
if (hasIndex && modName == "core")
{
// try to add H3 object data // try to add H3 object data
size_t index = static_cast<size_t>(data["index"].Float()); size_t index = static_cast<size_t>(data["index"].Float());

View File

@@ -117,7 +117,7 @@ void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const R
for(int i=0; i< info.reward.primary.size(); i++) for(int i=0; i< info.reward.primary.size(); i++)
cb->changePrimSkill(hero, static_cast<PrimarySkill>(i), info.reward.primary[i], false); cb->changePrimSkill(hero, static_cast<PrimarySkill>(i), info.reward.primary[i], false);
si64 expToGive = 0; TExpType expToGive = 0;
if (info.reward.heroLevel > 0) if (info.reward.heroLevel > 0)
expToGive += VLC->heroh->reqExp(hero->level+info.reward.heroLevel) - VLC->heroh->reqExp(hero->level); expToGive += VLC->heroh->reqExp(hero->level+info.reward.heroLevel) - VLC->heroh->reqExp(hero->level);
@@ -126,7 +126,7 @@ void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const R
expToGive += hero->calculateXp(info.reward.heroExperience); expToGive += hero->calculateXp(info.reward.heroExperience);
if(expToGive) if(expToGive)
cb->changePrimSkill(hero, PrimarySkill::EXPERIENCE, expToGive); cb->giveExperience(hero, expToGive);
} }
void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Rewardable::VisitInfo & info, const CArmedInstance * army, const CGHeroInstance * hero) const void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Rewardable::VisitInfo & info, const CArmedInstance * army, const CGHeroInstance * hero) const

View File

@@ -177,7 +177,7 @@ MainWindow::MainWindow(QWidget* parent) :
logGlobal->info("The log file will be saved to %s", logPath); logGlobal->info("The log file will be saved to %s", logPath);
//init //init
preinitDLL(::console, false, extractionOptions.extractArchives); preinitDLL(::console, extractionOptions.extractArchives);
// Initialize logging based on settings // Initialize logging based on settings
logConfig->configure(); logConfig->configure();

View File

@@ -365,52 +365,60 @@ void CGameHandler::expGiven(const CGHeroInstance *hero)
// levelUpHero(hero); // levelUpHero(hero);
} }
void CGameHandler::changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs) void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountToGain)
{ {
if (which == PrimarySkill::EXPERIENCE) // Check if scenario limit reached TExpType maxExp = VLC->heroh->reqExp(VLC->heroh->maxSupportedLevel());
{ TExpType currExp = hero->exp;
if (gs->map->levelLimit != 0)
{
TExpType expLimit = VLC->heroh->reqExp(gs->map->levelLimit);
TExpType resultingExp = abs ? val : hero->exp + val;
if (resultingExp > expLimit)
{
// set given experience to max possible, but don't decrease if hero already over top
abs = true;
val = std::max(expLimit, hero->exp);
InfoWindow iw; if (gs->map->levelLimit != 0)
iw.player = hero->tempOwner; maxExp = VLC->heroh->reqExp(gs->map->levelLimit);
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 1); //can gain no more XP
iw.text.replaceRawString(hero->getNameTranslated()); TExpType canGainExp = 0;
sendAndApply(&iw); if (maxExp > currExp)
} canGainExp = maxExp - currExp;
}
if (amountToGain > canGainExp)
{
// set given experience to max possible, but don't decrease if hero already over top
amountToGain = canGainExp;
InfoWindow iw;
iw.player = hero->tempOwner;
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 1); //can gain no more XP
iw.text.replaceRawString(hero->getNameTranslated());
sendAndApply(&iw);
} }
SetPrimSkill sps;
sps.id = hero->id;
sps.which = PrimarySkill::EXPERIENCE;
sps.abs = false;
sps.val = amountToGain;
sendAndApply(&sps);
//hero may level up
if (hero->commander && hero->commander->alive)
{
//FIXME: trim experience according to map limit?
SetCommanderProperty scp;
scp.heroid = hero->id;
scp.which = SetCommanderProperty::EXPERIENCE;
scp.amount = amountToGain;
sendAndApply (&scp);
CBonusSystemNode::treeHasChanged();
}
expGiven(hero);
}
void CGameHandler::changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs)
{
SetPrimSkill sps; SetPrimSkill sps;
sps.id = hero->id; sps.id = hero->id;
sps.which = which; sps.which = which;
sps.abs = abs; sps.abs = abs;
sps.val = val; sps.val = val;
sendAndApply(&sps); sendAndApply(&sps);
//only for exp - hero may level up
if (which == PrimarySkill::EXPERIENCE)
{
if (hero->commander && hero->commander->alive)
{
//FIXME: trim experience according to map limit?
SetCommanderProperty scp;
scp.heroid = hero->id;
scp.which = SetCommanderProperty::EXPERIENCE;
scp.amount = val;
sendAndApply (&scp);
CBonusSystemNode::treeHasChanged();
}
expGiven(hero);
}
} }
void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs) void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs)
@@ -658,7 +666,7 @@ void CGameHandler::onNewTurn()
{ {
if (obj && obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point if (obj && obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point
{ {
changePrimSkill (getHero(obj->id), PrimarySkill::EXPERIENCE, 0); giveExperience(getHero(obj->id), 0);
} }
} }
} }
@@ -3708,7 +3716,7 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan
int expSum = 0; int expSum = 0;
auto finish = [this, &hero, &expSum]() auto finish = [this, &hero, &expSum]()
{ {
changePrimSkill(hero, PrimarySkill::EXPERIENCE, hero->calculateXp(expSum)); giveExperience(hero, hero->calculateXp(expSum));
}; };
for(int i = 0; i < slot.size(); ++i) for(int i = 0; i < slot.size(); ++i)
@@ -3749,7 +3757,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
int expSum = 0; int expSum = 0;
auto finish = [this, &hero, &expSum]() auto finish = [this, &hero, &expSum]()
{ {
changePrimSkill(hero, PrimarySkill::EXPERIENCE, hero->calculateXp(expSum)); giveExperience(hero, hero->calculateXp(expSum));
}; };
for(int i = 0; i < slot.size(); ++i) for(int i = 0; i < slot.size(); ++i)

View File

@@ -102,6 +102,7 @@ public:
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override; bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override; void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override;
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override;
void giveExperience(const CGHeroInstance * hero, TExpType val) override;
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override;
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override;

View File

@@ -141,7 +141,11 @@ CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts)
if(cmdLineOptions.count("run-by-client")) if(cmdLineOptions.count("run-by-client"))
{ {
logNetwork->error("Port must be specified when run-by-client is used!!"); logNetwork->error("Port must be specified when run-by-client is used!!");
exit(0); #if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE)
::exit(0);
#else
std::quick_exit(0);
#endif
} }
acceptor = std::make_shared<TAcceptor>(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0)); acceptor = std::make_shared<TAcceptor>(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0));
port = acceptor->local_endpoint().port(); port = acceptor->local_endpoint().port();
@@ -1172,7 +1176,7 @@ int main(int argc, const char * argv[])
boost::program_options::variables_map opts; boost::program_options::variables_map opts;
handleCommandOptions(argc, argv, opts); handleCommandOptions(argc, argv, opts);
preinitDLL(console); preinitDLL(console, false);
logConfig.configure(); logConfig.configure();
loadDLLClasses(); loadDLLClasses();

View File

@@ -94,8 +94,14 @@ void TurnTimerHandler::update(int waitTime)
if(gs->isPlayerMakingTurn(player)) if(gs->isPlayerMakingTurn(player))
onPlayerMakingTurn(player, waitTime); onPlayerMakingTurn(player, waitTime);
// create copy for iterations - battle might end during onBattleLoop call
std::vector<BattleID> ongoingBattles;
for (auto & battle : gs->currentBattles) for (auto & battle : gs->currentBattles)
onBattleLoop(battle->battleID, waitTime); ongoingBattles.push_back(battle->battleID);
for (auto & battleID : ongoingBattles)
onBattleLoop(battleID, waitTime);
} }
} }

View File

@@ -712,6 +712,11 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
} }
} }
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER))); BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
bl.remove_if([](const Bonus * b)
{
return b->subtype.as<SpellID>() == SpellID::NONE;
});
int side = *battle.playerToSide(st->unitOwner()); int side = *battle.playerToSide(st->unitOwner());
if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0) if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0)
{ {

View File

@@ -494,7 +494,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
} }
//give exp //give exp
if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero) if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero)
gameHandler->changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult->exp[finishingBattle->winnerSide]); gameHandler->giveExperience(finishingBattle->winnerHero, battleResult->exp[finishingBattle->winnerSide]);
BattleResultAccepted raccepted; BattleResultAccepted raccepted;
raccepted.battleID = battle.getBattle()->getBattleID(); raccepted.battleID = battle.getBattle()->getBattleID();

View File

@@ -261,7 +261,7 @@ void PlayerMessageProcessor::cheatLevelup(PlayerColor player, const CGHeroInstan
levelsToGain = 1; levelsToGain = 1;
} }
gameHandler->changePrimSkill(hero, PrimarySkill::EXPERIENCE, VLC->heroh->reqExp(hero->level + levelsToGain) - VLC->heroh->reqExp(hero->level)); gameHandler->giveExperience(hero, VLC->heroh->reqExp(hero->level + levelsToGain) - VLC->heroh->reqExp(hero->level));
} }
void PlayerMessageProcessor::cheatExperience(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words) void PlayerMessageProcessor::cheatExperience(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words)
@@ -280,7 +280,7 @@ void PlayerMessageProcessor::cheatExperience(PlayerColor player, const CGHeroIns
expAmountProcessed = 10000; expAmountProcessed = 10000;
} }
gameHandler->changePrimSkill(hero, PrimarySkill::EXPERIENCE, expAmountProcessed); gameHandler->giveExperience(hero, expAmountProcessed);
} }
void PlayerMessageProcessor::cheatMovement(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words) void PlayerMessageProcessor::cheatMovement(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words)

View File

@@ -44,6 +44,7 @@ public:
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;} bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;}
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {}; void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {};
void setOwner(const CGObjectInstance * objid, PlayerColor owner) override {} void setOwner(const CGObjectInstance * objid, PlayerColor owner) override {}
void giveExperience(const CGHeroInstance * hero, TExpType val) override {}
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override {} void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override {}
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {} void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {}
void showBlockingDialog(BlockingDialog *iw) override {} void showBlockingDialog(BlockingDialog *iw) override {}