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:
@@ -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) {
|
||||
|
@@ -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 {};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
|
@@ -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 {}
|
||||
|
Reference in New Issue
Block a user