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);
// Init filesystem and settings
preinitDLL(::console);
preinitDLL(::console, false);
Settings session = settings.write["session"];
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;};
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) 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 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)
{
const auto * terrain = LOCPLINT->cb->getTile(currentSelection->visitablePos())->terType;
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
const auto * tile = LOCPLINT->cb->getTile(currentSelection->visitablePos());
if (tile)
CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false);
}
if(audioPlaying && enemyMakingTurn)

View File

@@ -69,7 +69,7 @@ int ISelectionScreenInfo::getCurrentDifficulty()
PlayerInfo ISelectionScreenInfo::getPlayerInfo(PlayerColor color)
{
return getMapInfo()->mapHeader->players[color.getNum()];
return getMapInfo()->mapHeader->players.at(color.getNum());
}
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)
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());
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());
return text;

View File

@@ -654,14 +654,21 @@ void CHeroHandler::loadExperience()
expPerLevel.push_back(24320);
expPerLevel.push_back(28784);
expPerLevel.push_back(34140);
while (expPerLevel[expPerLevel.size() - 1] > expPerLevel[expPerLevel.size() - 2])
for (;;)
{
auto i = expPerLevel.size() - 1;
auto diff = expPerLevel[i] - expPerLevel[i-1];
diff += diff / 5;
expPerLevel.push_back (expPerLevel[i] + diff);
auto currExp = expPerLevel[i];
auto prevExp = expPerLevel[i-1];
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)
@@ -741,12 +748,12 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
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));
}
ui64 CHeroHandler::reqExp (ui32 level) const
TExpType CHeroHandler::reqExp (ui32 level) const
{
if(!level)
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> result;

View File

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

View File

@@ -84,6 +84,7 @@ public:
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 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 changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
virtual void showBlockingDialog(BlockingDialog *iw) =0;

View File

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

View File

@@ -115,7 +115,7 @@ public:
// basic initialization. should be called before init(). Can also extract original H3 archives
void loadFilesystem(bool extractArchives);
void loadModFilesystem(bool onlyEssential);
void loadModFilesystem();
#if SCRIPTING_ENABLED
void scriptsLoaded();
@@ -124,7 +124,7 @@ public:
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);

View File

@@ -292,7 +292,7 @@ const Skill * SecondarySkill::toEntity(const Services * services) 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
@@ -324,12 +324,7 @@ std::string CreatureID::entityType()
const CSpell * SpellIDBase::toSpell() const
{
if(num < 0 || num >= VLC->spellh->objects.size())
{
logGlobal->error("Unable to get spell of invalid ID %d", static_cast<int>(num));
return nullptr;
}
return VLC->spellh->objects[num];
return dynamic_cast<const CSpell*>(toEntity(VLC));
}
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);
assert(object);
assert(obj->objects[index] == nullptr); // ensure that this id was not loaded before
obj->objects[index] = object;
if (obj->objects.at(index) != nullptr)
throw std::runtime_error("Attempt to load already loaded object:" + identifier);
obj->objects.at(index) = object;
registerObject(scope, obj->getJsonKey(), object->getSubTypeName(), object->subtype);
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;
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();
loadSubObject(subData.second.meta, subData.first, subData.second, obj.get(), subIndex);
if ( subMeta == "core")
{
size_t subIndex = subData.second["index"].Integer();
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
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)
{
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);
VLC->identifiersHandler->registerObject(scope, "object", name, objects[index]->id);
objects.at(index) = loadFromJson(scope, data, name, index);
VLC->identifiersHandler->registerObject(scope, "object", name, objects.at(index)->id);
}
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
assert(objects[ID.getNum()]);
assert(objects.at(ID.getNum()));
if ( subID.getNum() >= objects[ID.getNum()]->objects.size())
objects[ID.getNum()]->objects.resize(subID.getNum()+1);
if ( subID.getNum() >= objects.at(ID.getNum())->objects.size())
objects.at(ID.getNum())->objects.resize(subID.getNum()+1);
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)
{
assert(objects[ID.getNum()]);
objects[ID.getNum()]->objects[subID.getNum()] = nullptr;
assert(objects.at(ID.getNum()));
objects.at(ID.getNum())->objects.at(subID.getNum()) = nullptr;
}
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);
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);
if (subID)
return object->objects[subID.value()];
return object->objects.at(subID.value());
}
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())
return handler->getNameTranslated();
if (objects[type.getNum()])
return objects[type.getNum()]->getNameTranslated();
if (objects.at(type.getNum()))
return objects.at(type.getNum())->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)
subtype = 0;
if(objects[type.getNum()])
if(objects.at(type.getNum()))
return getHandlerFor(type, subtype)->getSounds();
else
return objects.front()->objects.front()->getSounds();

View File

@@ -1439,7 +1439,7 @@ void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ui8
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)

View File

@@ -249,8 +249,12 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
iw.player = cb->getOwner(heroID);
iw.text.appendRawString(getVisitingBonusGreeting());
cb->showInfoDialog(&iw);
cb->changePrimSkill (cb->getHero(heroID), what, val);
town->addHeroToStructureVisitors(h, indexOnTV);
if (what == PrimarySkill::EXPERIENCE)
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));
iw.text.appendLocalString(EMetaText::ADVOB_TXT,132);
iw.text.replaceNumber(static_cast<int>(xp));
cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, xp, false);
cb->giveExperience(h, xp);
}
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;
if(onlyEssential)
{
loadOneMod("vcmi", "", modConfig, true);//only vcmi and submods
}
else
{
modConfig = loadModSettings(JsonPath::builtin("config/modSettings.json"));
loadMods("", "", modConfig["activeMods"], true);
}
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")));
}
@@ -346,20 +339,25 @@ void CModHandler::loadModFilesystems()
TModID CModHandler::findResourceOrigin(const ResourcePath & name) const
{
for(const auto & modID : boost::adaptors::reverse(activeMods))
try
{
if(CResourceHandler::get(modID)->existsResource(name))
return modID;
for(const auto & modID : boost::adaptors::reverse(activeMods))
{
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
}
if(CResourceHandler::get("core")->existsResource(name))
return "core";
if(CResourceHandler::get("mapEditor")->existsResource(name))
return "core"; // Workaround for loading maps via map editor
assert(0);
return "";
catch( const std::out_of_range & e)
{
// no-op
}
throw std::runtime_error("Resource with name " + name.getName() + " and type " + EResTypeHelper::getEResTypeAsString(name.getType()) + " wasn't found.");
}
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
void initializeConfig();
void loadMods(bool onlyEssential = false);
void loadMods();
void loadModFilesystems();
/// 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;
}
if (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);
bool hasIndex = vstd::contains(data.Struct(), "index") && !data["index"].isNull();
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
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++)
cb->changePrimSkill(hero, static_cast<PrimarySkill>(i), info.reward.primary[i], false);
si64 expToGive = 0;
TExpType expToGive = 0;
if (info.reward.heroLevel > 0)
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);
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

View File

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

View File

@@ -365,52 +365,60 @@ void CGameHandler::expGiven(const CGHeroInstance *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
{
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);
TExpType maxExp = VLC->heroh->reqExp(VLC->heroh->maxSupportedLevel());
TExpType currExp = hero->exp;
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);
}
}
if (gs->map->levelLimit != 0)
maxExp = VLC->heroh->reqExp(gs->map->levelLimit);
TExpType canGainExp = 0;
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;
sps.id = hero->id;
sps.which = which;
sps.abs = abs;
sps.val = val;
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)
@@ -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
{
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;
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)
@@ -3749,7 +3757,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
int expSum = 0;
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)

View File

@@ -102,6 +102,7 @@ public:
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) 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 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"))
{
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));
port = acceptor->local_endpoint().port();
@@ -1172,7 +1176,7 @@ int main(int argc, const char * argv[])
boost::program_options::variables_map opts;
handleCommandOptions(argc, argv, opts);
preinitDLL(console);
preinitDLL(console, false);
logConfig.configure();
loadDLLClasses();

View File

@@ -94,8 +94,14 @@ void TurnTimerHandler::update(int waitTime)
if(gs->isPlayerMakingTurn(player))
onPlayerMakingTurn(player, waitTime);
// create copy for iterations - battle might end during onBattleLoop call
std::vector<BattleID> ongoingBattles;
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)));
bl.remove_if([](const Bonus * b)
{
return b->subtype.as<SpellID>() == SpellID::NONE;
});
int side = *battle.playerToSide(st->unitOwner());
if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0)
{

View File

@@ -494,7 +494,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
}
//give exp
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;
raccepted.battleID = battle.getBattle()->getBattleID();

View File

@@ -261,7 +261,7 @@ void PlayerMessageProcessor::cheatLevelup(PlayerColor player, const CGHeroInstan
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)
@@ -280,7 +280,7 @@ void PlayerMessageProcessor::cheatExperience(PlayerColor player, const CGHeroIns
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)

View File

@@ -44,6 +44,7 @@ public:
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 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 changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {}
void showBlockingDialog(BlockingDialog *iw) override {}