From ddff11184a3733ebc2b80522f6d6bdf48a4861e4 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Jul 2025 17:28:16 +0300 Subject: [PATCH 1/5] Split AdventureSpellMechanics file on .h+.cpp per class basis --- lib/CMakeLists.txt | 16 +- lib/spells/AdventureSpellMechanics.cpp | 782 ------------------ lib/spells/AdventureSpellMechanics.h | 123 --- lib/spells/ISpellMechanics.cpp | 8 +- .../adventure/AdventureSpellMechanics.cpp | 144 ++++ .../adventure/AdventureSpellMechanics.h | 46 ++ .../adventure/DimensionDoorMechanics.cpp | 166 ++++ lib/spells/adventure/DimensionDoorMechanics.h | 30 + lib/spells/adventure/ScuttleBoatMechanics.cpp | 78 ++ lib/spells/adventure/ScuttleBoatMechanics.h | 28 + lib/spells/adventure/SummonBoatMechanics.cpp | 111 +++ lib/spells/adventure/SummonBoatMechanics.h | 28 + lib/spells/adventure/TownPortalMechanics.cpp | 299 +++++++ lib/spells/adventure/TownPortalMechanics.h | 35 + lib/spells/adventure/ViewWorldMechanics.cpp | 90 ++ lib/spells/adventure/ViewWorldMechanics.h | 48 ++ 16 files changed, 1124 insertions(+), 908 deletions(-) delete mode 100644 lib/spells/AdventureSpellMechanics.cpp delete mode 100644 lib/spells/AdventureSpellMechanics.h create mode 100644 lib/spells/adventure/AdventureSpellMechanics.cpp create mode 100644 lib/spells/adventure/AdventureSpellMechanics.h create mode 100644 lib/spells/adventure/DimensionDoorMechanics.cpp create mode 100644 lib/spells/adventure/DimensionDoorMechanics.h create mode 100644 lib/spells/adventure/ScuttleBoatMechanics.cpp create mode 100644 lib/spells/adventure/ScuttleBoatMechanics.h create mode 100644 lib/spells/adventure/SummonBoatMechanics.cpp create mode 100644 lib/spells/adventure/SummonBoatMechanics.h create mode 100644 lib/spells/adventure/TownPortalMechanics.cpp create mode 100644 lib/spells/adventure/TownPortalMechanics.h create mode 100644 lib/spells/adventure/ViewWorldMechanics.cpp create mode 100644 lib/spells/adventure/ViewWorldMechanics.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9405dfe8d..5947d1b94 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -251,7 +251,6 @@ set(lib_MAIN_SRCS serializer/SerializerReflection.cpp spells/AbilityCaster.cpp - spells/AdventureSpellMechanics.cpp spells/BattleSpellMechanics.cpp spells/BonusCaster.cpp spells/CSpellHandler.cpp @@ -264,6 +263,13 @@ set(lib_MAIN_SRCS spells/TargetCondition.cpp spells/ViewSpellInt.cpp + spells/adventure/AdventureSpellMechanics.cpp + spells/adventure/DimensionDoorMechanics.cpp + spells/adventure/ScuttleBoatMechanics.cpp + spells/adventure/SummonBoatMechanics.cpp + spells/adventure/TownPortalMechanics.cpp + spells/adventure/ViewWorldMechanics.cpp + spells/effects/Catapult.cpp spells/effects/Clone.cpp spells/effects/Damage.cpp @@ -700,7 +706,6 @@ set(lib_MAIN_HEADERS serializer/SerializerReflection.h spells/AbilityCaster.h - spells/AdventureSpellMechanics.h spells/BattleSpellMechanics.h spells/BonusCaster.h spells/CSpellHandler.h @@ -713,6 +718,13 @@ set(lib_MAIN_HEADERS spells/TargetCondition.h spells/ViewSpellInt.h + spells/adventure/AdventureSpellMechanics.h + spells/adventure/DimensionDoorMechanics.h + spells/adventure/ScuttleBoatMechanics.h + spells/adventure/SummonBoatMechanics.h + spells/adventure/TownPortalMechanics.h + spells/adventure/ViewWorldMechanics.h + spells/effects/Catapult.h spells/effects/Clone.h spells/effects/Damage.h diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp deleted file mode 100644 index e57f7c3b0..000000000 --- a/lib/spells/AdventureSpellMechanics.cpp +++ /dev/null @@ -1,782 +0,0 @@ -/* - * AdventureSpellMechanics.cpp, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#include "StdInc.h" - -#include "AdventureSpellMechanics.h" - -#include "CSpellHandler.h" -#include "Problem.h" - -#include "../CPlayerState.h" -#include "../IGameSettings.h" -#include "../callback/IGameInfoCallback.h" -#include "../mapObjects/CGHeroInstance.h" -#include "../mapObjects/CGTownInstance.h" -#include "../mapObjects/MiscObjects.h" -#include "../mapping/CMap.h" -#include "../networkPacks/PacksForClient.h" - -#include - -VCMI_LIB_NAMESPACE_BEGIN - -///AdventureSpellMechanics -AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s): - IAdventureSpellMechanics(s) -{ -} - -bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!owner->isAdventure()) - return false; - - const auto * heroCaster = dynamic_cast(caster); - - if (heroCaster) - { - if(heroCaster->isGarrisoned()) - return false; - - const auto level = heroCaster->getSpellSchoolLevel(owner); - const auto cost = owner->getCost(level); - - if(!heroCaster->canCastThisSpell(owner)) - return false; - - if(heroCaster->mana < cost) - return false; - } - - return canBeCastImpl(problem, cb, caster); -} - -bool AdventureSpellMechanics::canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - return canBeCast(problem, cb, caster) && canBeCastAtImpl(problem, cb, caster, pos); -} - -bool AdventureSpellMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - return true; -} - -bool AdventureSpellMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - return true; -} - -bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - spells::detail::ProblemImpl problem; - - if (!canBeCastAt(problem, env->getCb(), parameters.caster, parameters.pos)) - return false; - - ESpellCastResult result = beginCast(env, parameters); - - if(result == ESpellCastResult::OK) - performCast(env, parameters); - - return result != ESpellCastResult::ERROR; -} - -ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - if(owner->hasEffects()) - { - //todo: cumulative effects support - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - std::vector bonuses; - - owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner)); - - for(const Bonus & b : bonuses) - { - GiveBonus gb; - gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); - gb.bonus = b; - env->apply(gb); - } - - return ESpellCastResult::OK; - } - else - { - //There is no generic algorithm of adventure cast - env->complain("Unimplemented adventure spell"); - return ESpellCastResult::ERROR; - } -} - -ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - return ESpellCastResult::OK; -} - -void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - // no-op, only for implementation in derived classes -} - -void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto level = parameters.caster->getSpellSchoolLevel(owner); - const auto cost = owner->getCost(level); - - AdvmapSpellCast asc; - asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId()); - asc.spellID = owner->id; - env->apply(asc); - - ESpellCastResult result = applyAdventureEffects(env, parameters); - - switch(result) - { - case ESpellCastResult::OK: - parameters.caster->spendMana(env, cost); - endCast(env, parameters); - break; - default: - break; - } -} - -///SummonBoatMechanics -SummonBoatMechanics::SummonBoatMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool SummonBoatMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!caster->getHeroCaster()) - return false; - - if(caster->getHeroCaster()->inBoat()) - { - MetaString message = MetaString::createFromTextID("core.genrltxt.333"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - int3 summonPos = caster->getHeroCaster()->bestLocation(); - - if(summonPos.x < 0) - { - MetaString message = MetaString::createFromTextID("core.genrltxt.334"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - return true; -} - -ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - //check if spell works at all - if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed. - parameters.caster->getCasterName(iw.text); - env->apply(iw); - return ESpellCastResult::OK; - } - - //try to find unoccupied boat to summon - const CGBoat * nearest = nullptr; - double dist = 0; - for(const auto & b : env->getMap()->getObjects()) - { - if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL) - continue; //we're looking for unoccupied boat - - double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos()); - if(!nearest || nDist < dist) //it's first boat or closer than previous - { - nearest = b; - dist = nDist; - } - } - - int3 summonPos = parameters.caster->getHeroCaster()->bestLocation(); - - if(nullptr != nearest) //we found boat to summon - { - ChangeObjPos cop; - cop.objid = nearest->id; - cop.nPos = summonPos; - cop.initiator = parameters.caster->getCasterOwner(); - env->apply(cop); - } - else if(schoolLevel < 2) //none or basic level -> cannot create boat :( - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon. - env->apply(iw); - return ESpellCastResult::ERROR; - } - else //create boat - { - env->createBoat(summonPos, BoatId::NECROPOLIS, parameters.caster->getCasterOwner()); - } - return ESpellCastResult::OK; -} - -///ScuttleBoatMechanics -ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool ScuttleBoatMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - if(!cb->isInTheMap(pos)) - return false; - - if (caster->getHeroCaster()) - { - int3 casterPosition = caster->getHeroCaster()->getSightCenter(); - - if(!isInScreenRange(casterPosition, pos)) - return false; - } - - if(!cb->isVisibleFor(pos, caster->getCasterOwner())) - return false; - - const TerrainTile * t = cb->getTile(pos); - if(!t || t->visitableObjects.empty()) - return false; - - const CGObjectInstance * topObject = cb->getObj(t->visitableObjects.back()); - if (topObject->ID != Obj::BOAT) - return false; - - return true; -} - -ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - //check if spell works at all - if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed - parameters.caster->getCasterName(iw.text); - env->apply(iw); - return ESpellCastResult::OK; - } - - const TerrainTile & t = env->getMap()->getTile(parameters.pos); - - RemoveObject ro; - ro.initiator = parameters.caster->getCasterOwner(); - ro.objectID = t.visitableObjects.back(); - env->apply(ro); - return ESpellCastResult::OK; -} - -///DimensionDoorMechanics -DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!caster->getHeroCaster()) - return false; - - if(caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough - { - problem.add(MetaString::createFromTextID("core.genrltxt.125")); - return false; - } - - const auto schoolLevel = caster->getSpellSchoolLevel(owner); - - std::stringstream cachingStr; - cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num; - - int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size(); - int castsLimit = owner->getLevelPower(schoolLevel); - - bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT); - if(isTournamentRulesLimitEnabled) - { - int3 mapSize = cb->getMapSize(); - - bool meetsTournamentRulesTwoCastsRequirements = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD - && schoolLevel == MasteryLevel::EXPERT; - - castsLimit = meetsTournamentRulesTwoCastsRequirements ? 2 : 1; - } - - if(castsAlreadyPerformedThisTurn >= castsLimit) //limit casts per turn - { - MetaString message = MetaString::createFromTextID("core.genrltxt.338"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - return true; -} - -bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - if(!cb->isInTheMap(pos)) - return false; - - if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES)) - { - if(!cb->isVisibleFor(pos, caster->getCasterOwner())) - return false; - } - - int3 casterPosition = caster->getHeroCaster()->getSightCenter(); - - const TerrainTile * dest = cb->getTileUnchecked(pos); - const TerrainTile * curr = cb->getTileUnchecked(casterPosition); - - if(!dest) - return false; - - if(!curr) - return false; - - if(!isInScreenRange(casterPosition, pos)) - return false; - - if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE)) - { - if(!dest->isClear(curr)) - return false; - } - else - { - if (dest->blocked()) - return false; - } - - return true; -} - -ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); - const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3); - - int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); - const TerrainTile * dest = env->getCb()->getTile(parameters.pos); - const TerrainTile * curr = env->getCb()->getTile(casterPosition); - - if(!dest->isClear(curr)) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - - // tile is either blocked or not possible to move (e.g. water <-> land) - if(env->getCb()->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS)) - { - // SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero - iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed! - env->apply(iw); - // no return - resources will be spent - } - else - { - // HotA: game will show error message without taking mana or move points, even when DD into terra incognita - iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError"); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - } - - GiveBonus gb; - gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); - gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id)); - env->apply(gb); - - SetMovePoints smp; - smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); - if(movementCost < static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining())) - smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost; - else - smp.val = 0; - env->apply(smp); - - return ESpellCastResult::OK; -} - -void DimensionDoorMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); - const TerrainTile * dest = env->getCb()->getTile(parameters.pos); - const TerrainTile * curr = env->getCb()->getTile(casterPosition); - - if(dest->isClear(curr)) - env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), EMovementMode::DIMENSION_DOOR); -} - -///TownPortalMechanics -TownPortalMechanics::TownPortalMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const CGTownInstance * destination = nullptr; - const int moveCost = movementCost(env, parameters); - - if(!parameters.caster->getHeroCaster()) - { - env->complain("Not a hero caster!"); - return ESpellCastResult::ERROR; - } - - if(parameters.caster->getSpellSchoolLevel(owner) < 2) - { - std::vector pool = getPossibleTowns(env, parameters); - destination = findNearestTown(env, parameters, pool); - - if(nullptr == destination) - return ESpellCastResult::ERROR; - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - return ESpellCastResult::ERROR; - - if(destination->getVisitingHero()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - } - else if(env->getMap()->isInTheMap(parameters.pos)) - { - const TerrainTile & tile = env->getMap()->getTile(parameters.pos); - - ObjectInstanceID topObjID = tile.topVisitableObj(false); - const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); - - if(!topObj) - { - env->complain("Destination tile is not visitable" + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - else if(topObj->ID == Obj::HERO) - { - env->complain("Can't teleport to occupied town at " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - else if(topObj->ID != Obj::TOWN) - { - env->complain("No town at destination tile " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - - destination = dynamic_cast(topObj); - - if(nullptr == destination) - { - env->complain("[Internal error] invalid town object at " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - - const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner()); - - if(relations == PlayerRelations::ENEMIES) - { - env->complain("Can't teleport to enemy!"); - return ESpellCastResult::ERROR; - } - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - { - env->complain("This hero has not enough movement points!"); - return ESpellCastResult::ERROR; - } - - if(destination->getVisitingHero()) - { - env->complain("[Internal error] Can't teleport to occupied town"); - return ESpellCastResult::ERROR; - } - } - else - { - env->complain("Invalid destination tile"); - return ESpellCastResult::ERROR; - } - - const TerrainTile & from = env->getMap()->getTile(parameters.caster->getHeroCaster()->visitablePos()); - const TerrainTile & dest = env->getMap()->getTile(destination->visitablePos()); - - if(!dest.entrableTerrain(&from)) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135); - env->apply(iw); - return ESpellCastResult::ERROR; - } - - return ESpellCastResult::OK; -} - -void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const int moveCost = movementCost(env, parameters); - const CGTownInstance * destination = nullptr; - - if(parameters.caster->getSpellSchoolLevel(owner) < 2) - { - std::vector pool = getPossibleTowns(env, parameters); - destination = findNearestTown(env, parameters, pool); - } - else - { - const TerrainTile & tile = env->getMap()->getTile(parameters.pos); - ObjectInstanceID topObjID = tile.topVisitableObj(false); - const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); - - destination = dynamic_cast(topObj); - } - - if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), EMovementMode::TOWN_PORTAL)) - { - SetMovePoints smp; - smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); - smp.val = std::max(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost); - env->apply(smp); - } -} - -ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - std::vector towns = getPossibleTowns(env, parameters); - - if(!parameters.caster->getHeroCaster()) - { - env->complain("Not a hero caster!"); - return ESpellCastResult::ERROR; - } - - if(towns.empty()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - const int moveCost = movementCost(env, parameters); - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - if(!parameters.pos.isValid() && parameters.caster->getSpellSchoolLevel(owner) >= 2) - { - auto queryCallback = [this, env, parameters](std::optional reply) -> void - { - if(reply.has_value()) - { - ObjectInstanceID townId(*reply); - - const CGObjectInstance * o = env->getCb()->getObj(townId, true); - if(o == nullptr) - { - env->complain("Invalid object instance selected"); - return; - } - - if(!dynamic_cast(o)) - { - env->complain("Object instance is not town"); - return; - } - - AdventureSpellCastParameters p; - p.caster = parameters.caster; - p.pos = o->visitablePos(); - performCast(env, p); - } - }; - - MapObjectSelectDialog request; - - for(const auto * t : towns) - { - if(t->getVisitingHero() == nullptr) //empty town - request.objects.push_back(t->id); - } - - if(request.objects.empty()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - request.player = parameters.caster->getCasterOwner(); - request.title.appendLocalString(EMetaText::JK_TXT, 40); - request.description.appendLocalString(EMetaText::JK_TXT, 41); - request.icon = Component(ComponentType::SPELL, owner->id); - - env->genericQuery(&request, request.player, queryCallback); - - return ESpellCastResult::PENDING; - } - - return ESpellCastResult::OK; -} - -const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const -{ - if(pool.empty()) - return nullptr; - - if(!parameters.caster->getHeroCaster()) - return nullptr; - - auto nearest = pool.cbegin(); //nearest town's iterator - si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); - - for(auto i = nearest + 1; i != pool.cend(); ++i) - { - si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); - - if(curDist < dist) - { - nearest = i; - dist = curDist; - } - } - return *nearest; -} - -std::vector TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - std::vector ret; - - const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner()); - - for(const auto & color : team->players) - { - for(auto currTown : env->getCb()->getPlayerState(color)->getTowns()) - { - ret.push_back(currTown); - } - } - return ret; -} - -int32_t TownPortalMechanics::movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero - return 0; - - int baseMovementCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); - return baseMovementCost * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); -} - -///ViewMechanics -ViewMechanics::ViewMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - ShowWorldViewEx pack; - - pack.player = parameters.caster->getCasterOwner(); - - const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); - - const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap; - - for(const auto & obj : env->getMap()->getObjects()) - { - //deleted object remain as empty pointer - if(obj && filterObject(obj, spellLevel)) - { - ObjectPosInfo posInfo(obj); - - if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0) - pack.objectPositions.push_back(posInfo); - } - } - pack.showTerrain = showTerrain(spellLevel); - - env->apply(pack); - - return ESpellCastResult::OK; -} - -///ViewAirMechanics -ViewAirMechanics::ViewAirMechanics(const CSpell * s): - ViewMechanics(s) -{ -} - -bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const -{ - return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); -} - -bool ViewAirMechanics::showTerrain(const int32_t spellLevel) const -{ - return false; -} - -///ViewEarthMechanics -ViewEarthMechanics::ViewEarthMechanics(const CSpell * s): - ViewMechanics(s) -{ -} - -bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const -{ - return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); -} - -bool ViewEarthMechanics::showTerrain(const int32_t spellLevel) const -{ - return spellLevel > 2; -} - -VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h deleted file mode 100644 index 247dcd9f4..000000000 --- a/lib/spells/AdventureSpellMechanics.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * AdventureSpellMechanics.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#pragma once - -#include "ISpellMechanics.h" - -VCMI_LIB_NAMESPACE_BEGIN - -class CGTownInstance; - -enum class ESpellCastResult -{ - OK, // cast successful - CANCEL, // cast failed but it is not an error, no mana has been spent - PENDING, - ERROR// error occurred, for example invalid request from player -}; - -class AdventureSpellMechanics : public IAdventureSpellMechanics -{ -public: - AdventureSpellMechanics(const CSpell * s); - - bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final; - bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final; - - bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; -protected: - ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const; - virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const; - - void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; -}; - -class SummonBoatMechanics final : public AdventureSpellMechanics -{ -public: - SummonBoatMechanics(const CSpell * s); -protected: - bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class ScuttleBoatMechanics final : public AdventureSpellMechanics -{ -public: - ScuttleBoatMechanics(const CSpell * s); -protected: - bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class DimensionDoorMechanics final : public AdventureSpellMechanics -{ -public: - DimensionDoorMechanics(const CSpell * s); -protected: - bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; - bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class TownPortalMechanics final : public AdventureSpellMechanics -{ -public: - TownPortalMechanics(const CSpell * s); -protected: - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -private: - const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; - int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - std::vector getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; -}; - -class ViewMechanics : public AdventureSpellMechanics -{ -public: - ViewMechanics(const CSpell * s); -protected: - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - virtual bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const = 0; - virtual bool showTerrain(const int32_t spellLevel) const = 0; -}; - -class ViewAirMechanics final : public ViewMechanics -{ -public: - ViewAirMechanics(const CSpell * s); -protected: - bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; - bool showTerrain(const int32_t spellLevel) const override; -}; - -class ViewEarthMechanics final : public ViewMechanics -{ -public: - ViewEarthMechanics(const CSpell * s); -protected: - bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; - bool showTerrain(const int32_t spellLevel) const override; -}; - - - -VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index d337de05e..192cbe064 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -26,7 +26,13 @@ #include "TargetCondition.h" #include "Problem.h" -#include "AdventureSpellMechanics.h" +#include "adventure/AdventureSpellMechanics.h" +#include "adventure/DimensionDoorMechanics.h" +#include "adventure/ScuttleBoatMechanics.h" +#include "adventure/SummonBoatMechanics.h" +#include "adventure/TownPortalMechanics.h" +#include "adventure/ViewWorldMechanics.h" + #include "BattleSpellMechanics.h" #include "effects/Effects.h" diff --git a/lib/spells/adventure/AdventureSpellMechanics.cpp b/lib/spells/adventure/AdventureSpellMechanics.cpp new file mode 100644 index 000000000..f5d5f6a46 --- /dev/null +++ b/lib/spells/adventure/AdventureSpellMechanics.cpp @@ -0,0 +1,144 @@ +/* + * AdventureSpellMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "AdventureSpellMechanics.h" + +#include "../CSpellHandler.h" +#include "../Problem.h" + +#include "../../mapObjects/CGHeroInstance.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s) + : IAdventureSpellMechanics(s) +{ +} + +bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!owner->isAdventure()) + return false; + + const auto * heroCaster = dynamic_cast(caster); + + if(heroCaster) + { + if(heroCaster->isGarrisoned()) + return false; + + const auto level = heroCaster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); + + if(!heroCaster->canCastThisSpell(owner)) + return false; + + if(heroCaster->mana < cost) + return false; + } + + return canBeCastImpl(problem, cb, caster); +} + +bool AdventureSpellMechanics::canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + return canBeCast(problem, cb, caster) && canBeCastAtImpl(problem, cb, caster, pos); +} + +bool AdventureSpellMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + return true; +} + +bool AdventureSpellMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + return true; +} + +bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + spells::detail::ProblemImpl problem; + + if(!canBeCastAt(problem, env->getCb(), parameters.caster, parameters.pos)) + return false; + + ESpellCastResult result = beginCast(env, parameters); + + if(result == ESpellCastResult::OK) + performCast(env, parameters); + + return result != ESpellCastResult::ERROR; +} + +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + if(owner->hasEffects()) + { + //todo: cumulative effects support + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + std::vector bonuses; + + owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner)); + + for(const Bonus & b : bonuses) + { + GiveBonus gb; + gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); + gb.bonus = b; + env->apply(gb); + } + + return ESpellCastResult::OK; + } + else + { + //There is no generic algorithm of adventure cast + env->complain("Unimplemented adventure spell"); + return ESpellCastResult::ERROR; + } +} + +ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + return ESpellCastResult::OK; +} + +void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + // no-op, only for implementation in derived classes +} + +void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto level = parameters.caster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); + + AdvmapSpellCast asc; + asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId()); + asc.spellID = owner->id; + env->apply(asc); + + ESpellCastResult result = applyAdventureEffects(env, parameters); + + switch(result) + { + case ESpellCastResult::OK: + parameters.caster->spendMana(env, cost); + endCast(env, parameters); + break; + default: + break; + } +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/AdventureSpellMechanics.h b/lib/spells/adventure/AdventureSpellMechanics.h new file mode 100644 index 000000000..ae352272e --- /dev/null +++ b/lib/spells/adventure/AdventureSpellMechanics.h @@ -0,0 +1,46 @@ +/* + * AdventureSpellMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "../ISpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +enum class ESpellCastResult +{ + OK, // cast successful + CANCEL, // cast failed but it is not an error, no mana has been spent + PENDING, + ERROR // error occurred, for example invalid request from player +}; + +class AdventureSpellMechanics : public IAdventureSpellMechanics +{ +public: + AdventureSpellMechanics(const CSpell * s); + + bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final; + bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final; + + bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; + +protected: + ///actual adventure cast implementation + virtual ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const; + virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const; + + void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/DimensionDoorMechanics.cpp b/lib/spells/adventure/DimensionDoorMechanics.cpp new file mode 100644 index 000000000..6198c5ff8 --- /dev/null +++ b/lib/spells/adventure/DimensionDoorMechanics.cpp @@ -0,0 +1,166 @@ +/* + * DimensionDoorMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "DimensionDoorMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/CMapDefines.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!caster->getHeroCaster()) + return false; + + if(caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough + { + problem.add(MetaString::createFromTextID("core.genrltxt.125")); + return false; + } + + const auto schoolLevel = caster->getSpellSchoolLevel(owner); + + std::stringstream cachingStr; + cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num; + + int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size(); + int castsLimit = owner->getLevelPower(schoolLevel); + + bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT); + if(isTournamentRulesLimitEnabled) + { + int3 mapSize = cb->getMapSize(); + + bool meetsTournamentRulesTwoCastsRequirements = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD && schoolLevel == MasteryLevel::EXPERT; + + castsLimit = meetsTournamentRulesTwoCastsRequirements ? 2 : 1; + } + + if(castsAlreadyPerformedThisTurn >= castsLimit) //limit casts per turn + { + MetaString message = MetaString::createFromTextID("core.genrltxt.338"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + return true; +} + +bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + if(!cb->isInTheMap(pos)) + return false; + + if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES)) + { + if(!cb->isVisibleFor(pos, caster->getCasterOwner())) + return false; + } + + int3 casterPosition = caster->getHeroCaster()->getSightCenter(); + + const TerrainTile * dest = cb->getTileUnchecked(pos); + const TerrainTile * curr = cb->getTileUnchecked(casterPosition); + + if(!dest) + return false; + + if(!curr) + return false; + + if(!isInScreenRange(casterPosition, pos)) + return false; + + if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE)) + { + if(!dest->isClear(curr)) + return false; + } + else + { + if(dest->blocked()) + return false; + } + + return true; +} + +ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); + const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3); + + int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); + const TerrainTile * dest = env->getCb()->getTile(parameters.pos); + const TerrainTile * curr = env->getCb()->getTile(casterPosition); + + if(!dest->isClear(curr)) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + + // tile is either blocked or not possible to move (e.g. water <-> land) + if(env->getCb()->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS)) + { + // SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero + iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed! + env->apply(iw); + // no return - resources will be spent + } + else + { + // HotA: game will show error message without taking mana or move points, even when DD into terra incognita + iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError"); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + } + + GiveBonus gb; + gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); + gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id)); + env->apply(gb); + + SetMovePoints smp; + smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); + if(movementCost < static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining())) + smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost; + else + smp.val = 0; + env->apply(smp); + + return ESpellCastResult::OK; +} + +void DimensionDoorMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); + const TerrainTile * dest = env->getCb()->getTile(parameters.pos); + const TerrainTile * curr = env->getCb()->getTile(casterPosition); + + if(dest->isClear(curr)) + env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), EMovementMode::DIMENSION_DOOR); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/DimensionDoorMechanics.h b/lib/spells/adventure/DimensionDoorMechanics.h new file mode 100644 index 000000000..cf5e32b6f --- /dev/null +++ b/lib/spells/adventure/DimensionDoorMechanics.h @@ -0,0 +1,30 @@ +/* + * DimensionDoorMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DimensionDoorMechanics final : public AdventureSpellMechanics +{ +public: + DimensionDoorMechanics(const CSpell * s); + +protected: + bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; + bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ScuttleBoatMechanics.cpp b/lib/spells/adventure/ScuttleBoatMechanics.cpp new file mode 100644 index 000000000..24b8bfe8b --- /dev/null +++ b/lib/spells/adventure/ScuttleBoatMechanics.cpp @@ -0,0 +1,78 @@ +/* + * ScuttleBoatMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "ScuttleBoatMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool ScuttleBoatMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + if(!cb->isInTheMap(pos)) + return false; + + if(caster->getHeroCaster()) + { + int3 casterPosition = caster->getHeroCaster()->getSightCenter(); + + if(!isInScreenRange(casterPosition, pos)) + return false; + } + + if(!cb->isVisibleFor(pos, caster->getCasterOwner())) + return false; + + const TerrainTile * t = cb->getTile(pos); + if(!t || t->visitableObjects.empty()) + return false; + + const CGObjectInstance * topObject = cb->getObj(t->visitableObjects.back()); + if(topObject->ID != Obj::BOAT) + return false; + + return true; +} + +ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + //check if spell works at all + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed + parameters.caster->getCasterName(iw.text); + env->apply(iw); + return ESpellCastResult::OK; + } + + const TerrainTile & t = env->getMap()->getTile(parameters.pos); + + RemoveObject ro; + ro.initiator = parameters.caster->getCasterOwner(); + ro.objectID = t.visitableObjects.back(); + env->apply(ro); + return ESpellCastResult::OK; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ScuttleBoatMechanics.h b/lib/spells/adventure/ScuttleBoatMechanics.h new file mode 100644 index 000000000..dec0b4037 --- /dev/null +++ b/lib/spells/adventure/ScuttleBoatMechanics.h @@ -0,0 +1,28 @@ +/* + * ScuttleBoatMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ScuttleBoatMechanics final : public AdventureSpellMechanics +{ +public: + ScuttleBoatMechanics(const CSpell * s); + +protected: + bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/SummonBoatMechanics.cpp b/lib/spells/adventure/SummonBoatMechanics.cpp new file mode 100644 index 000000000..6d4a0341d --- /dev/null +++ b/lib/spells/adventure/SummonBoatMechanics.cpp @@ -0,0 +1,111 @@ +/* + * SummonBoatMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" + +#include "SummonBoatMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapObjects/MiscObjects.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +SummonBoatMechanics::SummonBoatMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool SummonBoatMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!caster->getHeroCaster()) + return false; + + if(caster->getHeroCaster()->inBoat()) + { + MetaString message = MetaString::createFromTextID("core.genrltxt.333"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + int3 summonPos = caster->getHeroCaster()->bestLocation(); + + if(summonPos.x < 0) + { + MetaString message = MetaString::createFromTextID("core.genrltxt.334"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + return true; +} + +ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + //check if spell works at all + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed. + parameters.caster->getCasterName(iw.text); + env->apply(iw); + return ESpellCastResult::OK; + } + + //try to find unoccupied boat to summon + const CGBoat * nearest = nullptr; + double dist = 0; + for(const auto & b : env->getMap()->getObjects()) + { + if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL) + continue; //we're looking for unoccupied boat + + double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos()); + if(!nearest || nDist < dist) //it's first boat or closer than previous + { + nearest = b; + dist = nDist; + } + } + + int3 summonPos = parameters.caster->getHeroCaster()->bestLocation(); + + if(nullptr != nearest) //we found boat to summon + { + ChangeObjPos cop; + cop.objid = nearest->id; + cop.nPos = summonPos; + cop.initiator = parameters.caster->getCasterOwner(); + env->apply(cop); + } + else if(schoolLevel < 2) //none or basic level -> cannot create boat :( + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon. + env->apply(iw); + return ESpellCastResult::ERROR; + } + else //create boat + { + env->createBoat(summonPos, BoatId::NECROPOLIS, parameters.caster->getCasterOwner()); + } + return ESpellCastResult::OK; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/SummonBoatMechanics.h b/lib/spells/adventure/SummonBoatMechanics.h new file mode 100644 index 000000000..553abaa9c --- /dev/null +++ b/lib/spells/adventure/SummonBoatMechanics.h @@ -0,0 +1,28 @@ +/* + * SummonBoatMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class SummonBoatMechanics final : public AdventureSpellMechanics +{ +public: + SummonBoatMechanics(const CSpell * s); + +protected: + bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/TownPortalMechanics.cpp b/lib/spells/adventure/TownPortalMechanics.cpp new file mode 100644 index 000000000..e67df29d7 --- /dev/null +++ b/lib/spells/adventure/TownPortalMechanics.cpp @@ -0,0 +1,299 @@ +/* + * TownPortalMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "TownPortalMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../CPlayerState.h" +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapObjects/CGTownInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +TownPortalMechanics::TownPortalMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const CGTownInstance * destination = nullptr; + const int moveCost = movementCost(env, parameters); + + if(!parameters.caster->getHeroCaster()) + { + env->complain("Not a hero caster!"); + return ESpellCastResult::ERROR; + } + + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { + std::vector pool = getPossibleTowns(env, parameters); + destination = findNearestTown(env, parameters, pool); + + if(nullptr == destination) + return ESpellCastResult::ERROR; + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + return ESpellCastResult::ERROR; + + if(destination->getVisitingHero()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + } + else if(env->getMap()->isInTheMap(parameters.pos)) + { + const TerrainTile & tile = env->getMap()->getTile(parameters.pos); + + ObjectInstanceID topObjID = tile.topVisitableObj(false); + const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); + + if(!topObj) + { + env->complain("Destination tile is not visitable" + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + else if(topObj->ID == Obj::HERO) + { + env->complain("Can't teleport to occupied town at " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + else if(topObj->ID != Obj::TOWN) + { + env->complain("No town at destination tile " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + + destination = dynamic_cast(topObj); + + if(nullptr == destination) + { + env->complain("[Internal error] invalid town object at " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + + const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner()); + + if(relations == PlayerRelations::ENEMIES) + { + env->complain("Can't teleport to enemy!"); + return ESpellCastResult::ERROR; + } + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + { + env->complain("This hero has not enough movement points!"); + return ESpellCastResult::ERROR; + } + + if(destination->getVisitingHero()) + { + env->complain("[Internal error] Can't teleport to occupied town"); + return ESpellCastResult::ERROR; + } + } + else + { + env->complain("Invalid destination tile"); + return ESpellCastResult::ERROR; + } + + const TerrainTile & from = env->getMap()->getTile(parameters.caster->getHeroCaster()->visitablePos()); + const TerrainTile & dest = env->getMap()->getTile(destination->visitablePos()); + + if(!dest.entrableTerrain(&from)) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135); + env->apply(iw); + return ESpellCastResult::ERROR; + } + + return ESpellCastResult::OK; +} + +void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const int moveCost = movementCost(env, parameters); + const CGTownInstance * destination = nullptr; + + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { + std::vector pool = getPossibleTowns(env, parameters); + destination = findNearestTown(env, parameters, pool); + } + else + { + const TerrainTile & tile = env->getMap()->getTile(parameters.pos); + ObjectInstanceID topObjID = tile.topVisitableObj(false); + const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); + + destination = dynamic_cast(topObj); + } + + if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), EMovementMode::TOWN_PORTAL)) + { + SetMovePoints smp; + smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); + smp.val = std::max(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost); + env->apply(smp); + } +} + +ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + std::vector towns = getPossibleTowns(env, parameters); + + if(!parameters.caster->getHeroCaster()) + { + env->complain("Not a hero caster!"); + return ESpellCastResult::ERROR; + } + + if(towns.empty()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + const int moveCost = movementCost(env, parameters); + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + if(!parameters.pos.isValid() && parameters.caster->getSpellSchoolLevel(owner) >= 2) + { + auto queryCallback = [this, env, parameters](std::optional reply) -> void + { + if(reply.has_value()) + { + ObjectInstanceID townId(*reply); + + const CGObjectInstance * o = env->getCb()->getObj(townId, true); + if(o == nullptr) + { + env->complain("Invalid object instance selected"); + return; + } + + if(!dynamic_cast(o)) + { + env->complain("Object instance is not town"); + return; + } + + AdventureSpellCastParameters p; + p.caster = parameters.caster; + p.pos = o->visitablePos(); + performCast(env, p); + } + }; + + MapObjectSelectDialog request; + + for(const auto * t : towns) + { + if(t->getVisitingHero() == nullptr) //empty town + request.objects.push_back(t->id); + } + + if(request.objects.empty()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + request.player = parameters.caster->getCasterOwner(); + request.title.appendLocalString(EMetaText::JK_TXT, 40); + request.description.appendLocalString(EMetaText::JK_TXT, 41); + request.icon = Component(ComponentType::SPELL, owner->id); + + env->genericQuery(&request, request.player, queryCallback); + + return ESpellCastResult::PENDING; + } + + return ESpellCastResult::OK; +} + +const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const +{ + if(pool.empty()) + return nullptr; + + if(!parameters.caster->getHeroCaster()) + return nullptr; + + auto nearest = pool.cbegin(); //nearest town's iterator + si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); + + for(auto i = nearest + 1; i != pool.cend(); ++i) + { + si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); + + if(curDist < dist) + { + nearest = i; + dist = curDist; + } + } + return *nearest; +} + +std::vector TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + std::vector ret; + + const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner()); + + for(const auto & color : team->players) + { + for(auto currTown : env->getCb()->getPlayerState(color)->getTowns()) + { + ret.push_back(currTown); + } + } + return ret; +} + +int32_t TownPortalMechanics::movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero + return 0; + + int baseMovementCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); + return baseMovementCost * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/TownPortalMechanics.h b/lib/spells/adventure/TownPortalMechanics.h new file mode 100644 index 000000000..e0d227678 --- /dev/null +++ b/lib/spells/adventure/TownPortalMechanics.h @@ -0,0 +1,35 @@ +/* + * TownPortalMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGTownInstance; + +class TownPortalMechanics final : public AdventureSpellMechanics +{ +public: + TownPortalMechanics(const CSpell * s); + +protected: + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + +private: + const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; + int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + std::vector getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ViewWorldMechanics.cpp b/lib/spells/adventure/ViewWorldMechanics.cpp new file mode 100644 index 000000000..7d7c45aa9 --- /dev/null +++ b/lib/spells/adventure/ViewWorldMechanics.cpp @@ -0,0 +1,90 @@ +/* + * ViewWorldMechanics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" + +#include "ViewWorldMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../CPlayerState.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +ViewMechanics::ViewMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + ShowWorldViewEx pack; + + pack.player = parameters.caster->getCasterOwner(); + + const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); + + const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap; + + for(const auto & obj : env->getMap()->getObjects()) + { + //deleted object remain as empty pointer + if(obj && filterObject(obj, spellLevel)) + { + ObjectPosInfo posInfo(obj); + + if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0) + pack.objectPositions.push_back(posInfo); + } + } + pack.showTerrain = showTerrain(spellLevel); + + env->apply(pack); + + return ESpellCastResult::OK; +} + +///ViewAirMechanics +ViewAirMechanics::ViewAirMechanics(const CSpell * s) + : ViewMechanics(s) +{ +} + +bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const +{ + return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); +} + +bool ViewAirMechanics::showTerrain(const int32_t spellLevel) const +{ + return false; +} + +///ViewEarthMechanics +ViewEarthMechanics::ViewEarthMechanics(const CSpell * s) + : ViewMechanics(s) +{ +} + +bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const +{ + return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); +} + +bool ViewEarthMechanics::showTerrain(const int32_t spellLevel) const +{ + return spellLevel > 2; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ViewWorldMechanics.h b/lib/spells/adventure/ViewWorldMechanics.h new file mode 100644 index 000000000..2e82babf2 --- /dev/null +++ b/lib/spells/adventure/ViewWorldMechanics.h @@ -0,0 +1,48 @@ +/* + * ViewWorldMechanics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ViewMechanics : public AdventureSpellMechanics +{ +public: + ViewMechanics(const CSpell * s); + +protected: + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + virtual bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const = 0; + virtual bool showTerrain(const int32_t spellLevel) const = 0; +}; + +class ViewAirMechanics final : public ViewMechanics +{ +public: + ViewAirMechanics(const CSpell * s); + +protected: + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; + bool showTerrain(const int32_t spellLevel) const override; +}; + +class ViewEarthMechanics final : public ViewMechanics +{ +public: + ViewEarthMechanics(const CSpell * s); + +protected: + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; + bool showTerrain(const int32_t spellLevel) const override; +}; + +VCMI_LIB_NAMESPACE_END From 2ad186130f2e9b834a39fa0ddba17e2135618342 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Jul 2025 18:16:42 +0300 Subject: [PATCH 2/5] Split MapDefines file on .h per class basis --- AI/Nullkiller/AIGateway.cpp | 1 + AI/Nullkiller/AIUtility.cpp | 2 +- AI/Nullkiller/Analyzers/ArmyManager.cpp | 2 +- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 2 +- AI/Nullkiller/Pathfinding/Actors.cpp | 2 +- AI/VCAI/AIUtility.cpp | 2 +- AI/VCAI/Pathfinding/AIPathfinder.cpp | 3 +- AI/VCAI/Pathfinding/PathfindingManager.cpp | 1 - AI/VCAI/VCAI.cpp | 1 + client/CPlayerInterface.cpp | 1 + client/CServerHandler.cpp | 1 + client/Client.cpp | 1 + client/HeroMovementController.cpp | 1 + client/adventureMap/AdventureMapInterface.cpp | 2 +- client/adventureMap/CMinimap.cpp | 2 +- client/mapView/MapRenderer.cpp | 2 +- lib/CMakeLists.txt | 4 +- lib/gameState/CGameState.cpp | 1 + .../CommonConstructors.cpp | 2 +- lib/mapObjects/CArmedInstance.cpp | 2 +- lib/mapObjects/CGTownInstance.cpp | 1 + lib/mapObjects/IObjectInterface.cpp | 1 + lib/mapping/CCastleEvent.h | 37 +++++++++++ lib/mapping/CMap.cpp | 1 + lib/mapping/CMap.h | 3 +- lib/mapping/CMapEvent.h | 59 +++++++++++++++++ lib/mapping/MapFormatH3M.cpp | 1 + lib/mapping/{CMapDefines.h => TerrainTile.h} | 65 +------------------ lib/networkPacks/PacksForClient.h | 1 - lib/pathfinder/CGPathNode.cpp | 2 +- lib/pathfinder/PathfinderUtil.h | 2 +- lib/pathfinder/PathfindingRules.cpp | 2 +- lib/rewardable/Interface.cpp | 2 +- lib/serializer/SerializerReflection.cpp | 1 + .../adventure/DimensionDoorMechanics.cpp | 2 +- mapeditor/inspector/towneventswidget.cpp | 1 + mapeditor/inspector/towneventswidget.h | 1 - mapeditor/mapsettings/eventsettings.cpp | 1 - server/CVCMIServer.cpp | 1 + server/NetPacksLobbyServer.cpp | 1 + server/battles/BattleResultProcessor.cpp | 1 + server/processors/HeroPoolProcessor.cpp | 1 + server/processors/NewTurnProcessor.cpp | 1 + 43 files changed, 137 insertions(+), 86 deletions(-) create mode 100644 lib/mapping/CCastleEvent.h create mode 100644 lib/mapping/CMapEvent.h rename lib/mapping/{CMapDefines.h => TerrainTile.h} (73%) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index ea7113458..f43f29132 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -19,6 +19,7 @@ #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/CConfigHandler.h" #include "../../lib/IGameSettings.h" #include "../../lib/gameState/CGameState.h" diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index f6919ef46..6b2c389d4 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -17,7 +17,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/CQuest.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/gameState/QuestInfo.h" #include "../../lib/IGameSettings.h" #include "../../lib/bonuses/Limiters.h" diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index fd0951568..418a64cd8 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -12,7 +12,7 @@ #include "ArmyManager.h" #include "../Engine/Nullkiller.h" #include "../../../lib/mapObjects/MapObjects.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/IGameSettings.h" #include "../../../lib/GameConstants.h" #include "../../../lib/TerrainHandler.h" diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 6c189492d..668057fe0 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -15,7 +15,7 @@ #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h" #include "../../../lib/mapObjects/CGResource.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/RoadHandler.h" #include "../../../lib/CCreatureHandler.h" #include "../../../lib/GameLibrary.h" diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index 77070808b..7df7776e3 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -12,7 +12,7 @@ #include "../AIGateway.h" #include "../Engine/Nullkiller.h" #include "../../../lib/mapObjects/MapObjects.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/pathfinder/TurnInfo.h" #include "Actions/BuyArmyAction.h" diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 26847b838..6f412aabd 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -18,7 +18,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CQuest.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" extern FuzzyHelper * fh; diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index 799842892..768373ff2 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -10,7 +10,8 @@ #include "StdInc.h" #include "AIPathfinder.h" #include "AIPathfinderConfig.h" -#include "../../../lib/mapping/CMapDefines.h" + +#include "../../../lib/mapping/TerrainTile.h" #include diff --git a/AI/VCAI/Pathfinding/PathfindingManager.cpp b/AI/VCAI/Pathfinding/PathfindingManager.cpp index 54e3a2993..ed289593b 100644 --- a/AI/VCAI/Pathfinding/PathfindingManager.cpp +++ b/AI/VCAI/Pathfinding/PathfindingManager.cpp @@ -14,7 +14,6 @@ #include "../Goals/Goals.h" #include "../Goals/CompleteQuest.h" #include "../../../lib/gameState/QuestInfo.h" -#include "../../../lib/mapping/CMapDefines.h" #include "../../../lib/mapObjects/CQuest.h" PathfindingManager::PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 41a4026ea..ad1d5f935 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -33,6 +33,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/entities/building/CBuilding.h" #include "../../lib/mapObjects/CQuest.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/networkPacks/PacksForServer.h" diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 59a538548..b33d0cd94 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -68,6 +68,7 @@ #include "../lib/callback/CDynLibHandler.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/CPlayerState.h" #include "../lib/CRandomGenerator.h" diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 020e18ac7..99e24029d 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -32,6 +32,7 @@ #include "mainmenu/CHighScoreScreen.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/ConditionalWait.h" #include "../lib/CThreadHelper.h" diff --git a/client/Client.cpp b/client/Client.cpp index 54e9ce3ec..4ff098e8b 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -23,6 +23,7 @@ #include "mapView/mapHandler.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/battle/BattleInfo.h" #include "../lib/battle/CPlayerBattleCallback.h" #include "../lib/callback/CCallback.h" diff --git a/client/HeroMovementController.cpp b/client/HeroMovementController.cpp index 97ae56c71..e4e5ef58b 100644 --- a/client/HeroMovementController.cpp +++ b/client/HeroMovementController.cpp @@ -26,6 +26,7 @@ #include "../lib/callback/CCallback.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/mapObjects/CGHeroInstance.h" +#include "../lib/mapping/TerrainTile.h" #include "../lib/networkPacks/PacksForClient.h" #include "../lib/RoadHandler.h" #include "../lib/TerrainHandler.h" diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index ebe264fcd..363889b48 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -37,6 +37,7 @@ #include "../PlayerLocalState.h" #include "../CPlayerInterface.h" +#include "../../lib/GameLibrary.h" #include "../../lib/IGameSettings.h" #include "../../lib/StartInfo.h" #include "../../lib/callback/CCallback.h" @@ -44,7 +45,6 @@ #include "../../lib/spells/CSpellHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" -#include "../../lib/mapping/CMapDefines.h" #include "../../lib/pathfinder/CGPathNode.h" #include "../../lib/pathfinder/TurnInfo.h" #include "../../lib/spells/ISpellMechanics.h" diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index df1e38de4..54c70d928 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -30,7 +30,7 @@ #include "../../lib/TerrainHandler.h" #include "../../lib/callback/CCallback.h" #include "../../lib/mapObjects/CGHeroInstance.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/texts/CGeneralTextHandler.h" ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index f7c2c5d32..beeeb71d3 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -29,7 +29,7 @@ #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/pathfinder/CGPathNode.h" struct NeighborTilesInfo diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5947d1b94..70d6c67c0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -588,8 +588,9 @@ set(lib_MAIN_HEADERS mapObjects/ObstacleSetHandler.h mapping/CDrawRoadsOperation.h - mapping/CMapDefines.h + mapping/CCastleEvent.h mapping/CMapEditManager.h + mapping/CMapEvent.h mapping/CMapHeader.h mapping/CMap.h mapping/CMapInfo.h @@ -604,6 +605,7 @@ set(lib_MAIN_HEADERS mapping/MapReaderH3M.h mapping/MapFormatJson.h mapping/ObstacleProxy.h + mapping/TerrainTile.h modding/ActiveModsInSaveList.h modding/CModHandler.h diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 82af19c03..9a2c31e05 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -50,6 +50,7 @@ #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/MiscObjects.h" +#include "../mapping/CCastleEvent.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "../mapping/CMapService.h" diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index fecec9187..6d33d85c4 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -26,8 +26,8 @@ #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/MiscObjects.h" #include "../mapObjects/ObjectTemplate.h" +#include "../mapping/TerrainTile.h" #include "../modding/IdentifierStorage.h" -#include "../mapping/CMapDefines.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index ac0d54f50..a6a86ca58 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -17,9 +17,9 @@ #include "../entities/faction/CFaction.h" #include "../entities/faction/CTown.h" #include "../entities/faction/CTownHandler.h" +#include "../mapping/TerrainTile.h" #include "../GameLibrary.h" #include "../gameState/CGameState.h" -#include "../mapping/CMapDefines.h" #include "../texts/CGeneralTextHandler.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 0ad298a90..d700f402c 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -22,6 +22,7 @@ #include "../texts/CGeneralTextHandler.h" #include "../gameState/CGameState.h" #include "../gameState/UpgradeInfo.h" +#include "../mapping/CCastleEvent.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" #include "../StartInfo.h" diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index 18e9ab6bb..7e013b1d3 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -18,6 +18,7 @@ #include "../callback/IGameInfoCallback.h" #include "../callback/IGameEventCallback.h" #include "../mapObjects/CGHeroInstance.h" +#include "../mapping/TerrainTile.h" #include "../networkPacks/PacksForClient.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapping/CCastleEvent.h b/lib/mapping/CCastleEvent.h new file mode 100644 index 000000000..86f32ae1c --- /dev/null +++ b/lib/mapping/CCastleEvent.h @@ -0,0 +1,37 @@ +/* + * CCastleEvent.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "CMapEvent.h" + +VCMI_LIB_NAMESPACE_BEGIN + +/// The castle event builds/adds buildings/creatures for a specific town. +class DLL_LINKAGE CCastleEvent: public CMapEvent +{ +public: + CCastleEvent() = default; + + std::set buildings; + std::vector creatures; + + template + void serialize(Handler & h) + { + h & static_cast(*this); + h & buildings; + h & creatures; + } + + void serializeJson(JsonSerializeFormat & handler) override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index fabc7b167..145f17d4d 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -12,6 +12,7 @@ #include "CMapEditManager.h" #include "CMapOperation.h" +#include "CCastleEvent.h" #include "../CCreatureHandler.h" #include "../CSkillHandler.h" diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 21233f658..8544c82cb 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -10,8 +10,9 @@ #pragma once -#include "CMapDefines.h" +#include "CMapEvent.h" #include "CMapHeader.h" +#include "TerrainTile.h" #include "../mapObjects/CGObjectInstance.h" #include "../callback/GameCallbackHolder.h" diff --git a/lib/mapping/CMapEvent.h b/lib/mapping/CMapEvent.h new file mode 100644 index 000000000..a3c496077 --- /dev/null +++ b/lib/mapping/CMapEvent.h @@ -0,0 +1,59 @@ +/* + * CMapEvent.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "../ResourceSet.h" +#include "../texts/MetaString.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonSerializeFormat; + +/// The map event is an event which e.g. gives or takes resources of a specific +/// amount to/from players and can appear regularly or once a time. +class DLL_LINKAGE CMapEvent +{ +public: + CMapEvent(); + virtual ~CMapEvent() = default; + + bool occursToday(int currentDay) const; + bool affectsPlayer(PlayerColor player, bool isHuman) const; + + std::string name; + MetaString message; + TResources resources; + std::set players; + bool humanAffected; + bool computerAffected; + ui32 firstOccurrence; + ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time + + std::vector deletedObjectsInstances; + + template + void serialize(Handler & h) + { + h & name; + h & message; + h & resources; + h & players; + h & humanAffected; + h & computerAffected; + h & firstOccurrence; + h & nextOccurrence; + h & deletedObjectsInstances; + } + + virtual void serializeJson(JsonSerializeFormat & handler); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index c4d785d14..2147651e6 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "MapFormatH3M.h" +#include "CCastleEvent.h" #include "CMap.h" #include "MapReaderH3M.h" #include "MapFormatSettings.h" diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/TerrainTile.h similarity index 73% rename from lib/mapping/CMapDefines.h rename to lib/mapping/TerrainTile.h index d2df9203c..25f328eb2 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/TerrainTile.h @@ -1,5 +1,5 @@ /* - * CMapDefines.h, part of VCMI engine + * TerrainTile.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -10,8 +10,6 @@ #pragma once -#include "../ResourceSet.h" -#include "../texts/MetaString.h" #include "../GameLibrary.h" #include "../TerrainHandler.h" #include "../mapObjects/CGObjectInstance.h" @@ -22,67 +20,6 @@ class TerrainType; class RiverType; class RoadType; class CGObjectInstance; -class CGTownInstance; -class JsonSerializeFormat; - -/// The map event is an event which e.g. gives or takes resources of a specific -/// amount to/from players and can appear regularly or once a time. -class DLL_LINKAGE CMapEvent -{ -public: - CMapEvent(); - virtual ~CMapEvent() = default; - - bool occursToday(int currentDay) const; - bool affectsPlayer(PlayerColor player, bool isHuman) const; - - std::string name; - MetaString message; - TResources resources; - std::set players; - bool humanAffected; - bool computerAffected; - ui32 firstOccurrence; - ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time - - std::vector deletedObjectsInstances; - - template - void serialize(Handler & h) - { - h & name; - h & message; - h & resources; - h & players; - h & humanAffected; - h & computerAffected; - h & firstOccurrence; - h & nextOccurrence; - h & deletedObjectsInstances; - } - - virtual void serializeJson(JsonSerializeFormat & handler); -}; - -/// The castle event builds/adds buildings/creatures for a specific town. -class DLL_LINKAGE CCastleEvent: public CMapEvent -{ -public: - CCastleEvent() = default; - - std::set buildings; - std::vector creatures; - - template - void serialize(Handler & h) - { - h & static_cast(*this); - h & buildings; - h & creatures; - } - - void serializeJson(JsonSerializeFormat & handler) override; -}; /// The terrain tile describes the terrain type and the visual representation of the terrain. /// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it. diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 7a1165c5a..489ecafa5 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -26,7 +26,6 @@ #include "../gameState/TavernSlot.h" #include "../gameState/GameStatistics.h" #include "../int3.h" -#include "../mapping/CMapDefines.h" #include "../spells/ViewSpellInt.h" class CClient; diff --git a/lib/pathfinder/CGPathNode.cpp b/lib/pathfinder/CGPathNode.cpp index ca0e23366..755d47f32 100644 --- a/lib/pathfinder/CGPathNode.cpp +++ b/lib/pathfinder/CGPathNode.cpp @@ -14,7 +14,7 @@ #include "../callback/IGameInfoCallback.h" #include "../mapObjects/CGHeroInstance.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/pathfinder/PathfinderUtil.h b/lib/pathfinder/PathfinderUtil.h index d1e9b1faa..a28f50c34 100644 --- a/lib/pathfinder/PathfinderUtil.h +++ b/lib/pathfinder/PathfinderUtil.h @@ -10,7 +10,7 @@ #pragma once #include "../mapObjects/CGObjectInstance.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" #include "../callback/IGameInfoCallback.h" #include "CGPathNode.h" diff --git a/lib/pathfinder/PathfindingRules.cpp b/lib/pathfinder/PathfindingRules.cpp index 8f90a3fca..8b599126a 100644 --- a/lib/pathfinder/PathfindingRules.cpp +++ b/lib/pathfinder/PathfindingRules.cpp @@ -17,7 +17,7 @@ #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/MiscObjects.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Interface.cpp b/lib/rewardable/Interface.cpp index a0f69bbc3..b9c3c4103 100644 --- a/lib/rewardable/Interface.cpp +++ b/lib/rewardable/Interface.cpp @@ -22,7 +22,7 @@ #include "../spells/ISpellMechanics.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/MiscObjects.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" #include "../networkPacks/StackLocation.h" #include "../networkPacks/PacksForClient.h" diff --git a/lib/serializer/SerializerReflection.cpp b/lib/serializer/SerializerReflection.cpp index 9a4fc9c81..62d27c7b8 100644 --- a/lib/serializer/SerializerReflection.cpp +++ b/lib/serializer/SerializerReflection.cpp @@ -22,6 +22,7 @@ #include "../entities/hero/CHero.h" #include "../mapObjects/ObjectTemplate.h" #include "../mapping/CMapInfo.h" +#include "../mapping/CCastleEvent.h" #include "../rmg/CMapGenOptions.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/spells/adventure/DimensionDoorMechanics.cpp b/lib/spells/adventure/DimensionDoorMechanics.cpp index 6198c5ff8..72046d965 100644 --- a/lib/spells/adventure/DimensionDoorMechanics.cpp +++ b/lib/spells/adventure/DimensionDoorMechanics.cpp @@ -16,7 +16,7 @@ #include "../../IGameSettings.h" #include "../../callback/IGameInfoCallback.h" #include "../../mapObjects/CGHeroInstance.h" -#include "../../mapping/CMapDefines.h" +#include "../../mapping/TerrainTile.h" #include "../../networkPacks/PacksForClient.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/mapeditor/inspector/towneventswidget.cpp b/mapeditor/inspector/towneventswidget.cpp index 823c0525e..c6e303934 100644 --- a/mapeditor/inspector/towneventswidget.cpp +++ b/mapeditor/inspector/towneventswidget.cpp @@ -16,6 +16,7 @@ #include "mapsettings/eventsettings.h" #include "../../lib/constants/NumericConstants.h" #include "../../lib/constants/StringConstants.h" +#include "../../lib/mapping/CCastleEvent.h" TownEventsWidget::TownEventsWidget(CGTownInstance & town, QWidget * parent) : QDialog(parent), diff --git a/mapeditor/inspector/towneventswidget.h b/mapeditor/inspector/towneventswidget.h index 54e7e17b3..3bc1913a3 100644 --- a/mapeditor/inspector/towneventswidget.h +++ b/mapeditor/inspector/towneventswidget.h @@ -12,7 +12,6 @@ #include "../StdInc.h" #include #include "baseinspectoritemdelegate.h" -#include "../lib/mapping/CMapDefines.h" #include "../lib/mapObjects/CGTownInstance.h" #include "../mapcontroller.h" diff --git a/mapeditor/mapsettings/eventsettings.cpp b/mapeditor/mapsettings/eventsettings.cpp index a74cf5a21..7eae1b493 100644 --- a/mapeditor/mapsettings/eventsettings.cpp +++ b/mapeditor/mapsettings/eventsettings.cpp @@ -12,7 +12,6 @@ #include "timedevent.h" #include "ui_eventsettings.h" #include "../mapcontroller.h" -#include "../../lib/mapping/CMapDefines.h" #include "../../lib/constants/NumericConstants.h" #include "../../lib/constants/StringConstants.h" diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 802b5007b..4567ea2ce 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -16,6 +16,7 @@ #include "processors/PlayerMessageProcessor.h" #include "../lib/CThreadHelper.h" +#include "../lib/GameLibrary.h" #include "../lib/campaign/CampaignState.h" #include "../lib/entities/hero/CHeroHandler.h" #include "../lib/entities/hero/CHeroClass.h" diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index c1028aa5f..dafa74827 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/server/NetPacksLobbyServer.cpp @@ -14,6 +14,7 @@ #include "CGameHandler.h" #include "../lib/StartInfo.h" +#include "../lib/GameLibrary.h" #include "../lib/CRandomGenerator.h" #include "../lib/campaign/CampaignState.h" diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 16e638cca..e99b86858 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -17,6 +17,7 @@ #include "../queries/QueriesProcessor.h" #include "../queries/BattleQueries.h" +#include "../../lib/GameLibrary.h" #include "../../lib/CStack.h" #include "../../lib/CPlayerState.h" #include "../../lib/IGameSettings.h" diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index bdab46efb..04a6fa107 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -21,6 +21,7 @@ #include "../../lib/entities/hero/CHero.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/gameState/CGameState.h" #include "../../lib/gameState/TavernHeroesPool.h" diff --git a/server/processors/NewTurnProcessor.cpp b/server/processors/NewTurnProcessor.cpp index 33bb8eb03..846884fe0 100644 --- a/server/processors/NewTurnProcessor.cpp +++ b/server/processors/NewTurnProcessor.cpp @@ -27,6 +27,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/IOwnableObject.h" #include "../../lib/mapping/CMap.h" +#include "../../lib/mapping/CCastleEvent.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/StackLocation.h" #include "../../lib/pathfinder/TurnInfo.h" From 8721bdd72896ef0a135ef9f8d00c9ee4d8bc1e72 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Jul 2025 19:12:31 +0300 Subject: [PATCH 3/5] Split CCreatureSet file on .h/.cpp per class basis --- client/Client.cpp | 2 +- client/GameChatHandler.cpp | 2 +- client/UIHelper.cpp | 1 - client/adventureMap/CInGameConsole.cpp | 2 +- client/adventureMap/MapAudioPlayer.cpp | 1 - lib/CMakeLists.txt | 17 +- lib/battle/BattleLayout.cpp | 2 +- lib/battle/BattleStateInfoForRetreat.cpp | 1 - lib/battle/DamageCalculator.cpp | 1 + lib/bonuses/Bonus.cpp | 1 - lib/bonuses/Limiters.cpp | 1 - lib/gameState/EVictoryLossCheckResult.h | 2 + lib/gameState/InfoAboutArmy.h | 2 +- lib/json/JsonRandom.cpp | 2 +- .../DwellingInstanceConstructor.cpp | 1 + lib/mapObjects/CGCreature.h | 2 +- lib/mapObjects/CGDwelling.h | 3 +- lib/mapObjects/CGHeroInstance.h | 4 +- lib/mapObjects/CGResource.h | 2 +- lib/mapObjects/CGTownInstance.h | 2 +- lib/mapObjects/CRewardableObject.h | 3 +- lib/mapObjects/MapObjects.h | 1 - lib/mapObjects/MiscObjects.h | 3 +- lib/mapObjects/{ => army}/CArmedInstance.cpp | 18 +- lib/mapObjects/{ => army}/CArmedInstance.h | 12 +- lib/mapObjects/army/CCommanderInstance.cpp | 79 +++ lib/mapObjects/army/CCommanderInstance.h | 51 ++ lib/{ => mapObjects/army}/CCreatureSet.cpp | 498 +----------------- lib/{ => mapObjects/army}/CCreatureSet.h | 217 +------- lib/mapObjects/army/CSimpleArmy.h | 54 ++ lib/mapObjects/army/CStackBasicDescriptor.cpp | 90 ++++ lib/mapObjects/army/CStackBasicDescriptor.h | 64 +++ lib/mapObjects/army/CStackInstance.cpp | 356 +++++++++++++ lib/mapObjects/army/CStackInstance.h | 130 +++++ lib/networkPacks/PacksForClient.h | 3 +- lib/networkPacks/PacksForClientBattle.h | 1 + lib/rewardable/Info.cpp | 7 +- lib/rewardable/Interface.h | 2 + lib/rewardable/Limiter.h | 1 + lib/rewardable/Reward.h | 1 - lib/rmg/modificators/ObjectManager.cpp | 1 + lib/texts/MetaString.cpp | 2 +- mapeditor/inspector/armywidget.cpp | 1 + mapeditor/inspector/armywidget.h | 2 +- scripting/lua/api/StackInstance.h | 2 +- server/CGameHandler.cpp | 1 - server/queries/MapQueries.h | 1 + 47 files changed, 899 insertions(+), 753 deletions(-) rename lib/mapObjects/{ => army}/CArmedInstance.cpp (92%) rename lib/mapObjects/{ => army}/CArmedInstance.h (92%) create mode 100644 lib/mapObjects/army/CCommanderInstance.cpp create mode 100644 lib/mapObjects/army/CCommanderInstance.h rename lib/{ => mapObjects/army}/CCreatureSet.cpp (58%) rename lib/{ => mapObjects/army}/CCreatureSet.h (50%) create mode 100644 lib/mapObjects/army/CSimpleArmy.h create mode 100644 lib/mapObjects/army/CStackBasicDescriptor.cpp create mode 100644 lib/mapObjects/army/CStackBasicDescriptor.h create mode 100644 lib/mapObjects/army/CStackInstance.cpp create mode 100644 lib/mapObjects/army/CStackInstance.h diff --git a/client/Client.cpp b/client/Client.cpp index 4ff098e8b..d80c82769 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -36,8 +36,8 @@ #include "../lib/VCMIDirs.h" #include "../lib/UnlockGuard.h" #include "../lib/serializer/Connection.h" +#include "../lib/mapObjects/army/CArmedInstance.h" #include "../lib/mapping/CMapService.h" -#include "../lib/mapObjects/CArmedInstance.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/filesystem/Filesystem.h" diff --git a/client/GameChatHandler.cpp b/client/GameChatHandler.cpp index 92e3ac002..efd9c88e0 100644 --- a/client/GameChatHandler.cpp +++ b/client/GameChatHandler.cpp @@ -21,7 +21,7 @@ #include "../lib/callback/CCallback.h" #include "../lib/networkPacks/PacksForLobby.h" -#include "../lib/mapObjects/CArmedInstance.h" +#include "../lib/mapObjects/army/CArmedInstance.h" #include "../lib/CConfigHandler.h" #include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" diff --git a/client/UIHelper.cpp b/client/UIHelper.cpp index e5b36213b..25bf7e19a 100644 --- a/client/UIHelper.cpp +++ b/client/UIHelper.cpp @@ -16,7 +16,6 @@ #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/networkPacks/ArtifactLocation.h" #include "../lib/CRandomGenerator.h" -#include "../lib/CCreatureSet.h" std::vector UIHelper::getArtifactsComponents(const CArtifactSet & artSet, const std::vector & movedPack) { diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 1afb65466..88294b574 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -29,7 +29,7 @@ #include "../../lib/CConfigHandler.h" #include "../../lib/CThreadHelper.h" -#include "../../lib/mapObjects/CArmedInstance.h" +#include "../../lib/texts/MetaString.h" #include "../../lib/texts/TextOperations.h" CInGameConsole::CInGameConsole() diff --git a/client/adventureMap/MapAudioPlayer.cpp b/client/adventureMap/MapAudioPlayer.cpp index 419c4e31a..8b5246fb0 100644 --- a/client/adventureMap/MapAudioPlayer.cpp +++ b/client/adventureMap/MapAudioPlayer.cpp @@ -20,7 +20,6 @@ #include "../../lib/CRandomGenerator.h" #include "../../lib/TerrainHandler.h" #include "../../lib/callback/CCallback.h" -#include "../../lib/mapObjects/CArmedInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMap.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 70d6c67c0..59658a3ae 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -142,7 +142,6 @@ set(lib_MAIN_SRCS mapObjectConstructors/HillFortInstanceConstructor.cpp mapObjectConstructors/ShipyardInstanceConstructor.cpp - mapObjects/CArmedInstance.cpp mapObjects/CGCreature.cpp mapObjects/CGDwelling.cpp mapObjects/CGHeroInstance.cpp @@ -162,6 +161,12 @@ set(lib_MAIN_SRCS mapObjects/ObjectTemplate.cpp mapObjects/ObstacleSetHandler.cpp + mapObjects/army/CArmedInstance.cpp + mapObjects/army/CCommanderInstance.cpp + mapObjects/army/CCreatureSet.cpp + mapObjects/army/CStackBasicDescriptor.cpp + mapObjects/army/CStackInstance.cpp + mapping/CDrawRoadsOperation.cpp mapping/CMap.cpp mapping/CMapHeader.cpp @@ -299,7 +304,6 @@ set(lib_MAIN_SRCS CAndroidVMHelper.cpp CBonusTypeHandler.cpp CCreatureHandler.cpp - CCreatureSet.cpp CPlayerState.cpp CRandomGenerator.cpp CScriptingModule.cpp @@ -564,7 +568,6 @@ set(lib_MAIN_HEADERS mapObjectConstructors/ShipyardInstanceConstructor.h mapObjectConstructors/SObjectSounds.h - mapObjects/CArmedInstance.h mapObjects/CGCreature.h mapObjects/CGDwelling.h mapObjects/CGHeroInstance.h @@ -587,6 +590,13 @@ set(lib_MAIN_HEADERS mapObjects/ObjectTemplate.h mapObjects/ObstacleSetHandler.h + mapObjects/army/CArmedInstance.h + mapObjects/army/CCommanderInstance.h + mapObjects/army/CCreatureSet.h + mapObjects/army/CSimpleArmy.h + mapObjects/army/CStackBasicDescriptor.h + mapObjects/army/CStackInstance.h + mapping/CDrawRoadsOperation.h mapping/CCastleEvent.h mapping/CMapEditManager.h @@ -758,7 +768,6 @@ set(lib_MAIN_HEADERS CAndroidVMHelper.h CBonusTypeHandler.h CCreatureHandler.h - CCreatureSet.h ConditionalWait.h Color.h CPlayerState.h diff --git a/lib/battle/BattleLayout.cpp b/lib/battle/BattleLayout.cpp index 99e170134..a90f9e3c5 100644 --- a/lib/battle/BattleLayout.cpp +++ b/lib/battle/BattleLayout.cpp @@ -13,8 +13,8 @@ #include "../GameSettings.h" #include "../GameLibrary.h" #include "../callback/IGameInfoCallback.h" +#include "../mapObjects/army/CArmedInstance.h" #include "../json/JsonNode.h" -#include "../mapObjects/CArmedInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/battle/BattleStateInfoForRetreat.cpp b/lib/battle/BattleStateInfoForRetreat.cpp index a6f0118af..f933c0658 100644 --- a/lib/battle/BattleStateInfoForRetreat.cpp +++ b/lib/battle/BattleStateInfoForRetreat.cpp @@ -12,7 +12,6 @@ #include "BattleStateInfoForRetreat.h" #include "Unit.h" #include "CBattleInfoCallback.h" -#include "../CCreatureSet.h" #include "../mapObjects/CGHeroInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index 2d8020d2d..65ad1f772 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -15,6 +15,7 @@ #include "Unit.h" #include "../bonuses/Bonus.h" +#include "../CCreatureHandler.h" #include "../mapObjects/CGTownInstance.h" #include "../spells/CSpellHandler.h" #include "../IGameSettings.h" diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index 19bb36bfb..c4953edbe 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -16,7 +16,6 @@ #include "../CBonusTypeHandler.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../CSkillHandler.h" #include "../TerrainHandler.h" #include "../GameLibrary.h" diff --git a/lib/bonuses/Limiters.cpp b/lib/bonuses/Limiters.cpp index 70f9db377..567d86a98 100644 --- a/lib/bonuses/Limiters.cpp +++ b/lib/bonuses/Limiters.cpp @@ -16,7 +16,6 @@ #include "../GameLibrary.h" #include "../entities/faction/CTownHandler.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../CStack.h" #include "../TerrainHandler.h" #include "../constants/StringConstants.h" diff --git a/lib/gameState/EVictoryLossCheckResult.h b/lib/gameState/EVictoryLossCheckResult.h index f7860d78d..816faeccf 100644 --- a/lib/gameState/EVictoryLossCheckResult.h +++ b/lib/gameState/EVictoryLossCheckResult.h @@ -9,6 +9,8 @@ */ #pragma once +#include "../texts/MetaString.h" + VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE EVictoryLossCheckResult diff --git a/lib/gameState/InfoAboutArmy.h b/lib/gameState/InfoAboutArmy.h index bdbd47e9e..195e3d721 100644 --- a/lib/gameState/InfoAboutArmy.h +++ b/lib/gameState/InfoAboutArmy.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CCreatureSet.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/json/JsonRandom.cpp b/lib/json/JsonRandom.cpp index c70dd54d3..8f1e2878f 100644 --- a/lib/json/JsonRandom.cpp +++ b/lib/json/JsonRandom.cpp @@ -23,13 +23,13 @@ #include "../constants/StringConstants.h" #include "../GameLibrary.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../spells/CSpellHandler.h" #include "../CSkillHandler.h" #include "../entities/artifact/CArtHandler.h" #include "../entities/hero/CHero.h" #include "../entities/hero/CHeroClass.h" #include "../gameState/CGameState.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../mapObjects/IObjectInterface.h" #include "../modding/IdentifierStorage.h" #include "../modding/ModScope.h" diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp index a9c9b99fc..c468ea3e2 100644 --- a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp @@ -14,6 +14,7 @@ #include "../texts/CGeneralTextHandler.h" #include "../json/JsonRandom.h" #include "../GameLibrary.h" +#include "../mapObjects/army/CStackInstance.h" #include "../mapObjects/CGDwelling.h" #include "../mapObjects/ObjectTemplate.h" #include "../modding/IdentifierStorage.h" diff --git a/lib/mapObjects/CGCreature.h b/lib/mapObjects/CGCreature.h index 864d43b1e..fa2d49eaf 100644 --- a/lib/mapObjects/CGCreature.h +++ b/lib/mapObjects/CGCreature.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" #include "../ResourceSet.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGDwelling.h b/lib/mapObjects/CGDwelling.h index bf578f1d7..e69ce3dd1 100644 --- a/lib/mapObjects/CGDwelling.h +++ b/lib/mapObjects/CGDwelling.h @@ -10,9 +10,10 @@ #pragma once -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" + VCMI_LIB_NAMESPACE_BEGIN class CGDwelling; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index ed15184ff..df6e5c7e2 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -11,9 +11,11 @@ #include -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" +#include "army/CCommanderInstance.h" + #include "../bonuses/BonusCache.h" #include "../entities/hero/EHeroGender.h" diff --git a/lib/mapObjects/CGResource.h b/lib/mapObjects/CGResource.h index 5256943d1..5330b6c1d 100644 --- a/lib/mapObjects/CGResource.h +++ b/lib/mapObjects/CGResource.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index ff436df35..132a5f358 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -167,7 +167,7 @@ public: void removeAllBuildings(); std::set getBuildings() const; - TResources getBuildingCost(const BuildingID & buildingID) const; + ResourceSet getBuildingCost(const BuildingID & buildingID) const; ResourceSet dailyIncome() const override; std::vector providedCreatures() const override; diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 50e0a489f..ebc84883f 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -9,7 +9,8 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" + #include "../rewardable/Interface.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/MapObjects.h b/lib/mapObjects/MapObjects.h index 1ea66d139..918a05e14 100644 --- a/lib/mapObjects/MapObjects.h +++ b/lib/mapObjects/MapObjects.h @@ -13,7 +13,6 @@ // Possible TODO - remove this header after CObjectHandler.cpp will be fully split into smaller files #include "CObjectHandler.h" -#include "CArmedInstance.h" #include "CGDwelling.h" #include "CGHeroInstance.h" #include "CGMarket.h" diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index b62102e93..25c0fe5c2 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -9,8 +9,9 @@ */ #pragma once -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" +#include "../entities/artifact/CArtifactInstance.h" #include "../texts/MetaString.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/army/CArmedInstance.cpp similarity index 92% rename from lib/mapObjects/CArmedInstance.cpp rename to lib/mapObjects/army/CArmedInstance.cpp index a6a86ca58..44c3f60ef 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/army/CArmedInstance.cpp @@ -11,16 +11,14 @@ #include "StdInc.h" #include "CArmedInstance.h" -#include "../CCreatureHandler.h" -#include "../CPlayerState.h" -#include "../callback/IGameInfoCallback.h" -#include "../entities/faction/CFaction.h" -#include "../entities/faction/CTown.h" -#include "../entities/faction/CTownHandler.h" -#include "../mapping/TerrainTile.h" -#include "../GameLibrary.h" -#include "../gameState/CGameState.h" -#include "../texts/CGeneralTextHandler.h" +#include "CStackInstance.h" + +#include "../../CPlayerState.h" +#include "../../entities/faction/CTown.h" +#include "../../entities/faction/CTownHandler.h" +#include "../../mapping/TerrainTile.h" +#include "../../GameLibrary.h" +#include "../../gameState/CGameState.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CArmedInstance.h b/lib/mapObjects/army/CArmedInstance.h similarity index 92% rename from lib/mapObjects/CArmedInstance.h rename to lib/mapObjects/army/CArmedInstance.h index a5da54ee1..e465999bd 100644 --- a/lib/mapObjects/CArmedInstance.h +++ b/lib/mapObjects/army/CArmedInstance.h @@ -9,10 +9,14 @@ */ #pragma once -#include "CGObjectInstance.h" -#include "../CCreatureSet.h" -#include "../bonuses/CBonusSystemNode.h" -#include "../bonuses/BonusCache.h" +#include "CCreatureSet.h" + +#include "../CGObjectInstance.h" + +#include "../../bonuses/CBonusSystemNode.h" +#include "../../bonuses/BonusCache.h" + +#include VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/army/CCommanderInstance.cpp b/lib/mapObjects/army/CCommanderInstance.cpp new file mode 100644 index 000000000..ad7298e78 --- /dev/null +++ b/lib/mapObjects/army/CCommanderInstance.cpp @@ -0,0 +1,79 @@ +/* + * CCommanderInstance.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "CCommanderInstance.h" + +#include "../../GameLibrary.h" +#include "../../entities/hero/CHeroHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb) + :CStackInstance(cb) +{} + +CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id) + : CStackInstance(cb, BonusNodeType::COMMANDER, false) + , name("Commando") +{ + alive = true; + level = 1; + setCount(1); + setType(nullptr); + secondarySkills.resize (ECommander::SPELL_POWER + 1); + setType(id); + //TODO - parse them +} + +void CCommanderInstance::setAlive (bool Alive) +{ + //TODO: helm of immortality + alive = Alive; + if (!alive) + { + removeBonusesRecursive(Bonus::UntilCommanderKilled); + } +} + +bool CCommanderInstance::canGainExperience() const +{ + return alive; +} + +int CCommanderInstance::getExpRank() const +{ + return LIBRARY->heroh->level (getTotalExperience()); +} + +int CCommanderInstance::getLevel() const +{ + return std::max (1, getExpRank()); +} + +void CCommanderInstance::levelUp () +{ + level++; + for(const auto & bonus : LIBRARY->creh->commanderLevelPremy) + { //grant all regular level-up bonuses + accumulateBonus(bonus); + } +} + +ArtBearer CCommanderInstance::bearerType() const +{ + return ArtBearer::COMMANDER; +} + +bool CCommanderInstance::gainsLevel() const +{ + return getTotalExperience() >= LIBRARY->heroh->reqExp(level + 1); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CCommanderInstance.h b/lib/mapObjects/army/CCommanderInstance.h new file mode 100644 index 000000000..054071d61 --- /dev/null +++ b/lib/mapObjects/army/CCommanderInstance.h @@ -0,0 +1,51 @@ +/* + * CCommanderInstance.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "CStackInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DLL_LINKAGE CCommanderInstance : public CStackInstance +{ +public: + //TODO: what if Commander is not a part of creature set? + + //commander class is determined by its base creature + ui8 alive; //maybe change to bool when breaking save compatibility? + ui8 level; //required only to count callbacks + std::string name; // each Commander has different name + std::vector secondarySkills; //ID -> level + std::set specialSkills; + //std::vector arts; + CCommanderInstance(IGameInfoCallback *cb); + CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id); + void setAlive (bool alive); + void levelUp (); + + bool canGainExperience() const override; + bool gainsLevel() const; //true if commander has lower level than should upon his experience + ui64 getPower() const override {return 0;}; + int getExpRank() const override; + int getLevel() const override; + ArtBearer bearerType() const override; //from CArtifactSet + + template void serialize(Handler &h) + { + h & static_cast(*this); + h & alive; + h & level; + h & name; + h & secondarySkills; + h & specialSkills; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/CCreatureSet.cpp b/lib/mapObjects/army/CCreatureSet.cpp similarity index 58% rename from lib/CCreatureSet.cpp rename to lib/mapObjects/army/CCreatureSet.cpp index 80d8ac5b6..78f4fa90e 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/mapObjects/army/CCreatureSet.cpp @@ -10,22 +10,11 @@ #include "StdInc.h" #include "CCreatureSet.h" -#include "CConfigHandler.h" -#include "CCreatureHandler.h" -#include "GameLibrary.h" -#include "IGameSettings.h" -#include "callback/IGameInfoCallback.h" -#include "entities/hero/CHeroHandler.h" -#include "mapObjects/CGHeroInstance.h" -#include "modding/ModScope.h" -#include "texts/CGeneralTextHandler.h" -#include "spells/CSpellHandler.h" -#include "IBonusTypeHandler.h" -#include "serializer/JsonSerializeFormat.h" -#include "gameState/CGameState.h" +#include "../CGHeroInstance.h" -#include -#include +#include "../../CConfigHandler.h" +#include "../../texts/CGeneralTextHandler.h" +#include "../../serializer/JsonSerializeFormat.h" VCMI_LIB_NAMESPACE_BEGIN @@ -707,483 +696,4 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin } } -CStackInstance::CStackInstance(IGameInfoCallback *cb) - : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) -{} - -CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic) - : CBonusSystemNode(nodeType, isHypothetic) - , CStackBasicDescriptor(nullptr, 0) - , CArtifactSet(cb) - , GameCallbackHolder(cb) - , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE)) - , initiative(this, Selector::type()(BonusType::STACKS_SPEED)) - , totalExperience(0) -{} - -CStackInstance::CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity Count, bool isHypothetic) - : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) -{ - setType(id); - setCount(Count); -} - -CCreature::CreatureQuantityId CStackInstance::getQuantityID() const -{ - return CCreature::getQuantityID(getCount()); -} - -int CStackInstance::getExpRank() const -{ - if (!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) - return 0; - int tier = getType()->getLevel(); - if (vstd::iswithin(tier, 1, 7)) - { - for(int i = static_cast(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic! - { //exp values vary from 1st level to max exp at 11th level - if (getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) - return ++i; //faster, but confusing - 0 index mean 1st level of experience - } - return 0; - } - else //higher tier - { - for(int i = static_cast(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i) - { - if (getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) - return ++i; - } - return 0; - } -} - -int CStackInstance::getLevel() const -{ - return std::max(1, getType()->getLevel()); -} - -void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit) -{ - if (!canGainExperience()) - return; - - int level = std::clamp(getLevel(), 1, 7); - TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back(); - TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level]/100); - TExpType maxExperience = maxAmountPerUnit * getCount(); - TExpType maxExperienceToGain = maxExperience - totalExperience; - TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount()); - - totalExperience += actualGainedExperience; -} - -void CStackInstance::giveTotalStackExperience(TExpType experienceToGive) -{ - if (!canGainExperience()) - return; - - totalExperience += experienceToGive; -} - -TExpType CStackInstance::getTotalExperience() const -{ - return totalExperience; -} - -TExpType CStackInstance::getAverageExperience() const -{ - return totalExperience / getCount(); -} - -bool CStackInstance::canGainExperience() const -{ - return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE); -} - -void CStackInstance::setType(const CreatureID & creID) -{ - if (creID == CreatureID::NONE) - setType(nullptr);//FIXME: unused branch? - else - setType(creID.toCreature()); -} - -void CStackInstance::setType(const CCreature *c) -{ - if(getCreature()) - { - detachFromSource(*getCreature()); - if (LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) - totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100; - } - - CStackBasicDescriptor::setType(c); - - if(getCreature()) - attachToSource(*getCreature()); -} - -void CStackInstance::setCount(TQuantity newCount) -{ - assert(newCount >= 0); - - if (newCount < getCount()) - { - TExpType averageExperience = totalExperience / getCount(); - totalExperience = averageExperience * newCount; - } - - CStackBasicDescriptor::setCount(newCount); - nodeHasChanged(); -} - -std::string CStackInstance::bonusToString(const std::shared_ptr& bonus) const -{ - if (!bonus->description.empty()) - return bonus->description.toString(); - else - return LIBRARY->getBth()->bonusToString(bonus, this); -} - -ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr & bonus) const -{ - if (!bonus->customIconPath.empty()) - return bonus->customIconPath; - return LIBRARY->getBth()->bonusToGraphics(bonus); -} - -CArmedInstance * CStackInstance::getArmy() -{ - return armyInstance; -} - -const CArmedInstance * CStackInstance::getArmy() const -{ - return armyInstance; -} - -void CStackInstance::setArmy(CArmedInstance * ArmyObj) -{ - auto oldArmy = getArmy(); - - if(oldArmy) - { - detachFrom(*oldArmy); - armyInstance = nullptr; - } - - if(ArmyObj) - { - attachTo(const_cast(*ArmyObj)); - armyInstance = ArmyObj; - } -} - -std::string CStackInstance::getQuantityTXT(bool capitalized) const -{ - CCreature::CreatureQuantityId quantity = getQuantityID(); - - if ((int)quantity) - { - if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) - return CCreature::getQuantityRangeStringForId(quantity); - - return LIBRARY->generaltexth->arraytxt[174 + (int)quantity*3 - 1 - capitalized]; - } - else - return ""; -} - -bool CStackInstance::valid(bool allowUnrandomized) const -{ - if(!randomStack) - { - return (getType() && getType() == getId().toEntity(LIBRARY)); - } - else - return allowUnrandomized; -} - -std::string CStackInstance::nodeName() const -{ - std::ostringstream oss; - oss << "Stack of " << getCount() << " of "; - if(getType()) - oss << getType()->getNamePluralTextID(); - else - oss << "[UNDEFINED TYPE]"; - - return oss.str(); -} - -PlayerColor CStackInstance::getOwner() const -{ - auto army = getArmy(); - return army ? army->getOwner() : PlayerColor::NEUTRAL; -} - -int32_t CStackInstance::getInitiative(int turn) const -{ - if (turn == 0) - return initiative.getValue(); - - return ACreature::getInitiative(turn); -} - -TerrainId CStackInstance::getNativeTerrain() const -{ - if (nativeTerrain.hasBonus()) - return TerrainId::ANY_TERRAIN; - - return getFactionID().toEntity(LIBRARY)->getNativeTerrain(); -} - -TerrainId CStackInstance::getCurrentTerrain() const -{ - assert(getArmy() != nullptr); - return getArmy()->getCurrentTerrain(); -} - -CreatureID CStackInstance::getCreatureID() const -{ - if(getType()) - return getType()->getId(); - else - return CreatureID::NONE; -} - -std::string CStackInstance::getName() const -{ - return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated(); -} - -ui64 CStackInstance::getPower() const -{ - assert(getType()); - return static_cast(getType()->getAIValue()) * getCount(); -} - -ui64 CStackInstance::getMarketValue() const -{ - assert(getType()); - return getType()->getFullRecruitCost().marketValue() * getCount(); -} - -ArtBearer CStackInstance::bearerType() const -{ - return ArtBearer::CREATURE; -} - -CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) -{ - assert(!getArt(pos)); - assert(art->canBePutAt(this, pos)); - - attachToSource(*art); - return CArtifactSet::putArtifact(pos, art); -} - -void CStackInstance::removeArtifact(const ArtifactPosition & pos) -{ - assert(getArt(pos)); - - detachFromSource(*getArt(pos)); - CArtifactSet::removeArtifact(pos); -} - -void CStackInstance::serializeJson(JsonSerializeFormat & handler) -{ - //todo: artifacts - CStackBasicDescriptor::serializeJson(handler);//must be first - - if(handler.saving) - { - if(randomStack) - { - int level = randomStack->level; - int upgrade = randomStack->upgrade; - - handler.serializeInt("level", level, 0); - handler.serializeInt("upgraded", upgrade, 0); - } - } - else - { - //type set by CStackBasicDescriptor::serializeJson - if(getType() == nullptr) - { - uint8_t level = 0; - uint8_t upgrade = 0; - - handler.serializeInt("level", level, 0); - handler.serializeInt("upgrade", upgrade, 0); - - randomStack = RandomStackInfo{ level, upgrade }; - } - } -} - -FactionID CStackInstance::getFactionID() const -{ - if(getType()) - return getType()->getFactionID(); - - return FactionID::NEUTRAL; -} - -const IBonusBearer* CStackInstance::getBonusBearer() const -{ - return this; -} - -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb) - :CStackInstance(cb) -{} - -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id) - : CStackInstance(cb, BonusNodeType::COMMANDER, false) - , name("Commando") -{ - alive = true; - level = 1; - setCount(1); - setType(nullptr); - secondarySkills.resize (ECommander::SPELL_POWER + 1); - setType(id); - //TODO - parse them -} - -void CCommanderInstance::setAlive (bool Alive) -{ - //TODO: helm of immortality - alive = Alive; - if (!alive) - { - removeBonusesRecursive(Bonus::UntilCommanderKilled); - } -} - -bool CCommanderInstance::canGainExperience() const -{ - return alive; -} - -int CCommanderInstance::getExpRank() const -{ - return LIBRARY->heroh->level (getTotalExperience()); -} - -int CCommanderInstance::getLevel() const -{ - return std::max (1, getExpRank()); -} - -void CCommanderInstance::levelUp () -{ - level++; - for(const auto & bonus : LIBRARY->creh->commanderLevelPremy) - { //grant all regular level-up bonuses - accumulateBonus(bonus); - } -} - -ArtBearer CCommanderInstance::bearerType() const -{ - return ArtBearer::COMMANDER; -} - -bool CCommanderInstance::gainsLevel() const -{ - return getTotalExperience() >= LIBRARY->heroh->reqExp(level + 1); -} - -//This constructor should be placed here to avoid side effects -CStackBasicDescriptor::CStackBasicDescriptor() = default; - -CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count): - typeID(id), - count(Count) -{ -} - -CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count) - : typeID(c ? c->getId() : CreatureID()), count(Count) -{ -} - -const CCreature * CStackBasicDescriptor::getCreature() const -{ - return typeID.hasValue() ? typeID.toCreature() : nullptr; -} - -const Creature * CStackBasicDescriptor::getType() const -{ - return typeID.hasValue() ? typeID.toEntity(LIBRARY) : nullptr; -} - -CreatureID CStackBasicDescriptor::getId() const -{ - return typeID; -} - -TQuantity CStackBasicDescriptor::getCount() const -{ - return count; -} - -void CStackBasicDescriptor::setType(const CCreature * c) -{ - typeID = c ? c->getId() : CreatureID(); -} - -void CStackBasicDescriptor::setCount(TQuantity newCount) -{ - assert(newCount >= 0); - count = newCount; -} - -bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) -{ - return l.typeID == r.typeID && l.count == r.count; -} - -void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler) -{ - handler.serializeInt("amount", count); - - if(handler.saving) - { - if(typeID.hasValue()) - { - std::string typeName = typeID.toEntity(LIBRARY)->getJsonKey(); - handler.serializeString("type", typeName); - } - } - else - { - std::string typeName; - handler.serializeString("type", typeName); - if(!typeName.empty()) - setType(CreatureID(CreatureID::decode(typeName)).toCreature()); - } -} - -void CSimpleArmy::clearSlots() -{ - army.clear(); -} - -CSimpleArmy::operator bool() const -{ - return !army.empty(); -} - -bool CSimpleArmy::setCreature(SlotID slot, CreatureID cre, TQuantity count) -{ - assert(!vstd::contains(army, slot)); - army[slot] = std::make_pair(cre, count); - return true; -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/CCreatureSet.h b/lib/mapObjects/army/CCreatureSet.h similarity index 50% rename from lib/CCreatureSet.h rename to lib/mapObjects/army/CCreatureSet.h index b90c34378..67274238f 100644 --- a/lib/CCreatureSet.h +++ b/lib/mapObjects/army/CCreatureSet.h @@ -9,205 +9,18 @@ */ #pragma once -#include "CCreatureHandler.h" -#include "GameConstants.h" -#include "bonuses/Bonus.h" -#include "bonuses/BonusCache.h" -#include "bonuses/CBonusSystemNode.h" -#include "callback/GameCallbackHolder.h" -#include "serializer/Serializeable.h" -#include "mapObjects/CGObjectInstance.h" -#include "entities/artifact/CArtifactSet.h" +#include "CSimpleArmy.h" -#include +#include "serializer/Serializeable.h" VCMI_LIB_NAMESPACE_BEGIN -class JsonNode; -class CCreature; -class CGHeroInstance; +class CStackInstance; class CArmedInstance; -class CCreatureArtifactSet; +class CStackBasicDescriptor; class JsonSerializeFormat; -class DLL_LINKAGE CStackBasicDescriptor -{ - CreatureID typeID; - TQuantity count = -1; //exact quantity or quantity ID from CCreature::getQuantityID when getting info about enemy army - -public: - CStackBasicDescriptor(); - CStackBasicDescriptor(const CreatureID & id, TQuantity Count); - CStackBasicDescriptor(const CCreature *c, TQuantity Count); - virtual ~CStackBasicDescriptor() = default; - - const Creature * getType() const; - const CCreature * getCreature() const; - CreatureID getId() const; - TQuantity getCount() const; - - virtual void setType(const CCreature * c); - virtual void setCount(TQuantity amount); - - friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); - - template void serialize(Handler &h) - { - if(h.saving) - { - h & typeID; - } - else - { - CreatureID creatureID; - h & creatureID; - if(creatureID != CreatureID::NONE) - setType(creatureID.toCreature()); - } - - h & count; - } - - void serializeJson(JsonSerializeFormat & handler); -}; - -class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public ACreature, public GameCallbackHolder -{ - BonusValueCache nativeTerrain; - BonusValueCache initiative; - - CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object - - IGameInfoCallback * getCallback() const final { return cb; } - - TExpType totalExperience;//commander needs same amount of exp as hero -public: - struct RandomStackInfo - { - uint8_t level; - uint8_t upgrade; - }; - // helper variable used during loading map, when object (hero or town) have creatures that must have same alignment. - std::optional randomStack; - - CArmedInstance * getArmy(); - const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object - void setArmy(CArmedInstance *ArmyObj); - - TExpType getTotalExperience() const; - TExpType getAverageExperience() const; - virtual bool canGainExperience() const; - - template void serialize(Handler &h) - { - h & static_cast(*this); - h & static_cast(*this); - h & static_cast(*this); - - if (h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) - { - // no-op - } - if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) - { - ObjectInstanceID dummyID; - h & dummyID; - } - else - { - std::shared_ptr army; - h & army; - } - - h & totalExperience; - if (!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) - { - totalExperience *= getCount(); - } - } - - void serializeJson(JsonSerializeFormat & handler); - - //overrides CBonusSystemNode - std::string bonusToString(const std::shared_ptr& bonus) const override; // how would bonus description look for this particular type of node - ImagePath bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others - - //IConstBonusProvider - const IBonusBearer* getBonusBearer() const override; - //INativeTerrainProvider - FactionID getFactionID() const override; - - virtual ui64 getPower() const; - /// Returns total market value of resources needed to recruit this unit - virtual ui64 getMarketValue() const; - CCreature::CreatureQuantityId getQuantityID() const; - std::string getQuantityTXT(bool capitalized = true) const; - virtual int getExpRank() const; - virtual int getLevel() const; //different for regular stack and commander - CreatureID getCreatureID() const; //-1 if not available - std::string getName() const; //plural or singular - CStackInstance(IGameInfoCallback *cb); - CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic = false); - CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); - virtual ~CStackInstance() = default; - - void setType(const CreatureID & creID); - void setType(const CCreature * c) final; - void setCount(TQuantity amount) final; - - /// Gives specified amount of stack experience that will not be scaled by unit size - void giveAverageStackExperience(TExpType exp); - void giveTotalStackExperience(TExpType exp); - - bool valid(bool allowUnrandomized) const; - ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet - void removeArtifact(const ArtifactPosition & pos) override; - ArtBearer bearerType() const override; //from CArtifactSet - std::string nodeName() const override; //from CBonusSystemnode - PlayerColor getOwner() const override; - - int32_t getInitiative(int turn = 0) const final; - TerrainId getNativeTerrain() const final; - TerrainId getCurrentTerrain() const; -}; - -class DLL_LINKAGE CCommanderInstance : public CStackInstance -{ -public: - //TODO: what if Commander is not a part of creature set? - - //commander class is determined by its base creature - ui8 alive; //maybe change to bool when breaking save compatibility? - ui8 level; //required only to count callbacks - std::string name; // each Commander has different name - std::vector secondarySkills; //ID -> level - std::set specialSkills; - //std::vector arts; - CCommanderInstance(IGameInfoCallback *cb); - CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id); - void setAlive (bool alive); - void levelUp (); - - bool canGainExperience() const override; - bool gainsLevel() const; //true if commander has lower level than should upon his experience - ui64 getPower() const override {return 0;}; - int getExpRank() const override; - int getLevel() const override; - ArtBearer bearerType() const override; //from CArtifactSet - - template void serialize(Handler &h) - { - h & static_cast(*this); - h & alive; - h & level; - h & name; - h & secondarySkills; - h & specialSkills; - } -}; - using TSlots = std::map>; -using TSimpleSlots = std::map>; using TPairCreatureSlot = std::pair; using TMapCreatureSlot = std::map; @@ -219,28 +32,6 @@ struct DLL_LINKAGE CreatureSlotComparer using TCreatureQueue = std::priority_queue, CreatureSlotComparer>; -class IArmyDescriptor -{ -public: - virtual void clearSlots() = 0; - virtual bool setCreature(SlotID slot, CreatureID cre, TQuantity count) = 0; -}; - -//simplified version of CCreatureSet -class DLL_LINKAGE CSimpleArmy : public IArmyDescriptor -{ -public: - TSimpleSlots army; - void clearSlots() override; - bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override; - operator bool() const; - - template void serialize(Handler &h) - { - h & army; - } -}; - namespace NArmyFormation { static const std::vector names{ "wide", "tight" }; diff --git a/lib/mapObjects/army/CSimpleArmy.h b/lib/mapObjects/army/CSimpleArmy.h new file mode 100644 index 000000000..8fb57f695 --- /dev/null +++ b/lib/mapObjects/army/CSimpleArmy.h @@ -0,0 +1,54 @@ +/* + * CSimpleArmy.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "GameConstants.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class IArmyDescriptor +{ +public: + virtual void clearSlots() = 0; + virtual bool setCreature(SlotID slot, CreatureID cre, TQuantity count) = 0; +}; + +using TSimpleSlots = std::map>; + +//simplified version of CCreatureSet +class DLL_LINKAGE CSimpleArmy : public IArmyDescriptor +{ +public: + TSimpleSlots army; + void clearSlots() override + { + army.clear(); + } + + bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override + { + assert(!vstd::contains(army, slot)); + army[slot] = std::make_pair(cre, count); + return true; + } + + operator bool() const + { + return !army.empty(); + } + + + template void serialize(Handler &h) + { + h & army; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackBasicDescriptor.cpp b/lib/mapObjects/army/CStackBasicDescriptor.cpp new file mode 100644 index 000000000..15d19aba2 --- /dev/null +++ b/lib/mapObjects/army/CStackBasicDescriptor.cpp @@ -0,0 +1,90 @@ +/* + * CStackBasicDescriptor.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "CStackBasicDescriptor.h" + +#include "../../CCreatureHandler.h" +#include "../../GameLibrary.h" +#include "../../serializer/JsonSerializeFormat.h" + +VCMI_LIB_NAMESPACE_BEGIN + +//This constructor should be placed here to avoid side effects +CStackBasicDescriptor::CStackBasicDescriptor() = default; + +CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count): + typeID(id), + count(Count) +{ +} + +CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count) + : typeID(c ? c->getId() : CreatureID()), count(Count) +{ +} + +const CCreature * CStackBasicDescriptor::getCreature() const +{ + return typeID.hasValue() ? typeID.toCreature() : nullptr; +} + +const Creature * CStackBasicDescriptor::getType() const +{ + return typeID.hasValue() ? typeID.toEntity(LIBRARY) : nullptr; +} + +CreatureID CStackBasicDescriptor::getId() const +{ + return typeID; +} + +TQuantity CStackBasicDescriptor::getCount() const +{ + return count; +} + +void CStackBasicDescriptor::setType(const CCreature * c) +{ + typeID = c ? c->getId() : CreatureID(); +} + +void CStackBasicDescriptor::setCount(TQuantity newCount) +{ + assert(newCount >= 0); + count = newCount; +} + +bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) +{ + return l.typeID == r.typeID && l.count == r.count; +} + +void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler) +{ + handler.serializeInt("amount", count); + + if(handler.saving) + { + if(typeID.hasValue()) + { + std::string typeName = typeID.toEntity(LIBRARY)->getJsonKey(); + handler.serializeString("type", typeName); + } + } + else + { + std::string typeName; + handler.serializeString("type", typeName); + if(!typeName.empty()) + setType(CreatureID(CreatureID::decode(typeName)).toCreature()); + } +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackBasicDescriptor.h b/lib/mapObjects/army/CStackBasicDescriptor.h new file mode 100644 index 000000000..a89d5ff70 --- /dev/null +++ b/lib/mapObjects/army/CStackBasicDescriptor.h @@ -0,0 +1,64 @@ +/* + * CStackBasicDescriptor.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "GameConstants.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonNode; +class CCreature; +class CGHeroInstance; +class CArmedInstance; +class CCreatureArtifactSet; +class JsonSerializeFormat; + +class DLL_LINKAGE CStackBasicDescriptor +{ + CreatureID typeID; + TQuantity count = -1; //exact quantity or quantity ID from CCreature::getQuantityID when getting info about enemy army + +public: + CStackBasicDescriptor(); + CStackBasicDescriptor(const CreatureID & id, TQuantity Count); + CStackBasicDescriptor(const CCreature *c, TQuantity Count); + virtual ~CStackBasicDescriptor() = default; + + const Creature * getType() const; + const CCreature * getCreature() const; + CreatureID getId() const; + TQuantity getCount() const; + + virtual void setType(const CCreature * c); + virtual void setCount(TQuantity amount); + + friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); + + template void serialize(Handler &h) + { + if(h.saving) + { + h & typeID; + } + else + { + CreatureID creatureID; + h & creatureID; + if(creatureID != CreatureID::NONE) + setType(creatureID.toCreature()); + } + + h & count; + } + + void serializeJson(JsonSerializeFormat & handler); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackInstance.cpp b/lib/mapObjects/army/CStackInstance.cpp new file mode 100644 index 000000000..c964a9311 --- /dev/null +++ b/lib/mapObjects/army/CStackInstance.cpp @@ -0,0 +1,356 @@ +/* + * CStackInstance.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "CStackInstance.h" + +#include "CArmedInstance.h" + +#include "../../CConfigHandler.h" +#include "../../GameLibrary.h" +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../entities/faction/CFaction.h" +#include "../../texts/CGeneralTextHandler.h" +#include "../../IBonusTypeHandler.h" +#include "../../serializer/JsonSerializeFormat.h" + +VCMI_LIB_NAMESPACE_BEGIN + +CStackInstance::CStackInstance(IGameInfoCallback *cb) + : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) +{} + +CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic) + : CBonusSystemNode(nodeType, isHypothetic) + , CStackBasicDescriptor(nullptr, 0) + , CArtifactSet(cb) + , GameCallbackHolder(cb) + , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE)) + , initiative(this, Selector::type()(BonusType::STACKS_SPEED)) + , totalExperience(0) +{} + +CStackInstance::CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity Count, bool isHypothetic) + : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) +{ + setType(id); + setCount(Count); +} + +CCreature::CreatureQuantityId CStackInstance::getQuantityID() const +{ + return CCreature::getQuantityID(getCount()); +} + +int CStackInstance::getExpRank() const +{ + if (!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + return 0; + int tier = getType()->getLevel(); + if (vstd::iswithin(tier, 1, 7)) + { + for(int i = static_cast(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic! + { //exp values vary from 1st level to max exp at 11th level + if (getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) + return ++i; //faster, but confusing - 0 index mean 1st level of experience + } + return 0; + } + else //higher tier + { + for(int i = static_cast(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i) + { + if (getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) + return ++i; + } + return 0; + } +} + +int CStackInstance::getLevel() const +{ + return std::max(1, getType()->getLevel()); +} + +void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit) +{ + if (!canGainExperience()) + return; + + int level = std::clamp(getLevel(), 1, 7); + TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back(); + TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level]/100); + TExpType maxExperience = maxAmountPerUnit * getCount(); + TExpType maxExperienceToGain = maxExperience - totalExperience; + TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount()); + + totalExperience += actualGainedExperience; +} + +void CStackInstance::giveTotalStackExperience(TExpType experienceToGive) +{ + if (!canGainExperience()) + return; + + totalExperience += experienceToGive; +} + +TExpType CStackInstance::getTotalExperience() const +{ + return totalExperience; +} + +TExpType CStackInstance::getAverageExperience() const +{ + return totalExperience / getCount(); +} + +bool CStackInstance::canGainExperience() const +{ + return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE); +} + +void CStackInstance::setType(const CreatureID & creID) +{ + if (creID == CreatureID::NONE) + setType(nullptr);//FIXME: unused branch? + else + setType(creID.toCreature()); +} + +void CStackInstance::setType(const CCreature *c) +{ + if(getCreature()) + { + detachFromSource(*getCreature()); + if (LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100; + } + + CStackBasicDescriptor::setType(c); + + if(getCreature()) + attachToSource(*getCreature()); +} + +void CStackInstance::setCount(TQuantity newCount) +{ + assert(newCount >= 0); + + if (newCount < getCount()) + { + TExpType averageExperience = totalExperience / getCount(); + totalExperience = averageExperience * newCount; + } + + CStackBasicDescriptor::setCount(newCount); + nodeHasChanged(); +} + +std::string CStackInstance::bonusToString(const std::shared_ptr& bonus) const +{ + if (!bonus->description.empty()) + return bonus->description.toString(); + else + return LIBRARY->getBth()->bonusToString(bonus, this); +} + +ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr & bonus) const +{ + if (!bonus->customIconPath.empty()) + return bonus->customIconPath; + return LIBRARY->getBth()->bonusToGraphics(bonus); +} + +CArmedInstance * CStackInstance::getArmy() +{ + return armyInstance; +} + +const CArmedInstance * CStackInstance::getArmy() const +{ + return armyInstance; +} + +void CStackInstance::setArmy(CArmedInstance * ArmyObj) +{ + auto oldArmy = getArmy(); + + if(oldArmy) + { + detachFrom(*oldArmy); + armyInstance = nullptr; + } + + if(ArmyObj) + { + attachTo(const_cast(*ArmyObj)); + armyInstance = ArmyObj; + } +} + +std::string CStackInstance::getQuantityTXT(bool capitalized) const +{ + CCreature::CreatureQuantityId quantity = getQuantityID(); + + if ((int)quantity) + { + if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) + return CCreature::getQuantityRangeStringForId(quantity); + + return LIBRARY->generaltexth->arraytxt[174 + (int)quantity*3 - 1 - capitalized]; + } + else + return ""; +} + +bool CStackInstance::valid(bool allowUnrandomized) const +{ + if(!randomStack) + { + return (getType() && getType() == getId().toEntity(LIBRARY)); + } + else + return allowUnrandomized; +} + +std::string CStackInstance::nodeName() const +{ + std::ostringstream oss; + oss << "Stack of " << getCount() << " of "; + if(getType()) + oss << getType()->getNamePluralTextID(); + else + oss << "[UNDEFINED TYPE]"; + + return oss.str(); +} + +PlayerColor CStackInstance::getOwner() const +{ + auto army = getArmy(); + return army ? army->getOwner() : PlayerColor::NEUTRAL; +} + +int32_t CStackInstance::getInitiative(int turn) const +{ + if (turn == 0) + return initiative.getValue(); + + return ACreature::getInitiative(turn); +} + +TerrainId CStackInstance::getNativeTerrain() const +{ + if (nativeTerrain.hasBonus()) + return TerrainId::ANY_TERRAIN; + + return getFactionID().toEntity(LIBRARY)->getNativeTerrain(); +} + +TerrainId CStackInstance::getCurrentTerrain() const +{ + assert(getArmy() != nullptr); + return getArmy()->getCurrentTerrain(); +} + +CreatureID CStackInstance::getCreatureID() const +{ + if(getType()) + return getType()->getId(); + else + return CreatureID::NONE; +} + +std::string CStackInstance::getName() const +{ + return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated(); +} + +ui64 CStackInstance::getPower() const +{ + assert(getType()); + return static_cast(getType()->getAIValue()) * getCount(); +} + +ui64 CStackInstance::getMarketValue() const +{ + assert(getType()); + return getType()->getFullRecruitCost().marketValue() * getCount(); +} + +ArtBearer CStackInstance::bearerType() const +{ + return ArtBearer::CREATURE; +} + +CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) +{ + assert(!getArt(pos)); + assert(art->canBePutAt(this, pos)); + + attachToSource(*art); + return CArtifactSet::putArtifact(pos, art); +} + +void CStackInstance::removeArtifact(const ArtifactPosition & pos) +{ + assert(getArt(pos)); + + detachFromSource(*getArt(pos)); + CArtifactSet::removeArtifact(pos); +} + +void CStackInstance::serializeJson(JsonSerializeFormat & handler) +{ + //todo: artifacts + CStackBasicDescriptor::serializeJson(handler);//must be first + + if(handler.saving) + { + if(randomStack) + { + int level = randomStack->level; + int upgrade = randomStack->upgrade; + + handler.serializeInt("level", level, 0); + handler.serializeInt("upgraded", upgrade, 0); + } + } + else + { + //type set by CStackBasicDescriptor::serializeJson + if(getType() == nullptr) + { + uint8_t level = 0; + uint8_t upgrade = 0; + + handler.serializeInt("level", level, 0); + handler.serializeInt("upgrade", upgrade, 0); + + randomStack = RandomStackInfo{ level, upgrade }; + } + } +} + +FactionID CStackInstance::getFactionID() const +{ + if(getType()) + return getType()->getFactionID(); + + return FactionID::NEUTRAL; +} + +const IBonusBearer* CStackInstance::getBonusBearer() const +{ + return this; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackInstance.h b/lib/mapObjects/army/CStackInstance.h new file mode 100644 index 000000000..7c84a8d2c --- /dev/null +++ b/lib/mapObjects/army/CStackInstance.h @@ -0,0 +1,130 @@ +/* + * CStackInstance.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "CStackBasicDescriptor.h" + +#include "CCreatureHandler.h" +#include "bonuses/BonusCache.h" +#include "bonuses/CBonusSystemNode.h" +#include "callback/GameCallbackHolder.h" +#include "mapObjects/CGObjectInstance.h" +#include "entities/artifact/CArtifactSet.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonNode; +class CCreature; +class CGHeroInstance; +class CArmedInstance; +class CCreatureArtifactSet; +class JsonSerializeFormat; + +class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public ACreature, public GameCallbackHolder +{ + BonusValueCache nativeTerrain; + BonusValueCache initiative; + + CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object + + IGameInfoCallback * getCallback() const final { return cb; } + + TExpType totalExperience;//commander needs same amount of exp as hero +public: + struct RandomStackInfo + { + uint8_t level; + uint8_t upgrade; + }; + // helper variable used during loading map, when object (hero or town) have creatures that must have same alignment. + std::optional randomStack; + + CArmedInstance * getArmy(); + const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object + void setArmy(CArmedInstance *ArmyObj); + + TExpType getTotalExperience() const; + TExpType getAverageExperience() const; + virtual bool canGainExperience() const; + + template void serialize(Handler &h) + { + h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); + + if (h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) + { + // no-op + } + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + ObjectInstanceID dummyID; + h & dummyID; + } + else + { + std::shared_ptr army; + h & army; + } + + h & totalExperience; + if (!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) + { + totalExperience *= getCount(); + } + } + + void serializeJson(JsonSerializeFormat & handler); + + //overrides CBonusSystemNode + std::string bonusToString(const std::shared_ptr& bonus) const override; // how would bonus description look for this particular type of node + ImagePath bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others + + //IConstBonusProvider + const IBonusBearer* getBonusBearer() const override; + //INativeTerrainProvider + FactionID getFactionID() const override; + + virtual ui64 getPower() const; + /// Returns total market value of resources needed to recruit this unit + virtual ui64 getMarketValue() const; + CCreature::CreatureQuantityId getQuantityID() const; + std::string getQuantityTXT(bool capitalized = true) const; + virtual int getExpRank() const; + virtual int getLevel() const; //different for regular stack and commander + CreatureID getCreatureID() const; //-1 if not available + std::string getName() const; //plural or singular + CStackInstance(IGameInfoCallback *cb); + CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic = false); + CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); + virtual ~CStackInstance() = default; + + void setType(const CreatureID & creID); + void setType(const CCreature * c) final; + void setCount(TQuantity amount) final; + + /// Gives specified amount of stack experience that will not be scaled by unit size + void giveAverageStackExperience(TExpType exp); + void giveTotalStackExperience(TExpType exp); + + bool valid(bool allowUnrandomized) const; + ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet + void removeArtifact(const ArtifactPosition & pos) override; + ArtBearer bearerType() const override; //from CArtifactSet + std::string nodeName() const override; //from CBonusSystemnode + PlayerColor getOwner() const override; + + int32_t getInitiative(int turn = 0) const final; + TerrainId getNativeTerrain() const final; + TerrainId getCurrentTerrain() const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 489ecafa5..94ad81c2d 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -17,15 +17,16 @@ #include "NetPacksBase.h" #include "ObjProperty.h" -#include "../CCreatureSet.h" #include "../ResourceSet.h" #include "../TurnTimerInfo.h" +#include "../bonuses/Bonus.h" #include "../gameState/EVictoryLossCheckResult.h" #include "../gameState/RumorState.h" #include "../gameState/QuestInfo.h" #include "../gameState/TavernSlot.h" #include "../gameState/GameStatistics.h" #include "../int3.h" +#include "../mapObjects/army/CSimpleArmy.h" #include "../spells/ViewSpellInt.h" class CClient; diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index cbe7fadbb..0af85b92f 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -16,6 +16,7 @@ #include "../battle/BattleInfo.h" #include "../battle/BattleHexArray.h" #include "../battle/BattleUnitTurnReason.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../texts/MetaString.h" class CClient; diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index ebdf658fa..7f60b5041 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -15,12 +15,13 @@ #include "Limiter.h" #include "Reward.h" -#include "../callback/IGameRandomizer.h" -#include "../texts/CGeneralTextHandler.h" -#include "../json/JsonRandom.h" +#include "../CCreatureHandler.h" #include "../GameLibrary.h" +#include "../callback/IGameRandomizer.h" +#include "../json/JsonRandom.h" #include "../mapObjects/IObjectInterface.h" #include "../modding/IdentifierStorage.h" +#include "../texts/CGeneralTextHandler.h" #include diff --git a/lib/rewardable/Interface.h b/lib/rewardable/Interface.h index eb54f2a3a..3cb329c3c 100644 --- a/lib/rewardable/Interface.h +++ b/lib/rewardable/Interface.h @@ -16,6 +16,8 @@ VCMI_LIB_NAMESPACE_BEGIN class IObjectInterface; +class IGameEventCallback; +class CArmedInstance; namespace Rewardable { diff --git a/lib/rewardable/Limiter.h b/lib/rewardable/Limiter.h index 9e1682aa6..e75b98ee2 100644 --- a/lib/rewardable/Limiter.h +++ b/lib/rewardable/Limiter.h @@ -12,6 +12,7 @@ #include "../GameConstants.h" #include "../ResourceSet.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../serializer/Serializeable.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Reward.h b/lib/rewardable/Reward.h index 83a70123a..2f9d896ca 100644 --- a/lib/rewardable/Reward.h +++ b/lib/rewardable/Reward.h @@ -12,7 +12,6 @@ #include "../ResourceSet.h" #include "../bonuses/Bonus.h" -#include "../CCreatureSet.h" #include "../networkPacks/Component.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 5d614eaad..f0c4a4521 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -23,6 +23,7 @@ #include "../../CCreatureHandler.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" +#include "../../mapObjects/army/CStackInstance.h" #include "../../mapObjects/CGCreature.h" #include "../../mapping/CMap.h" #include "../../mapping/CMapEditManager.h" diff --git a/lib/texts/MetaString.cpp b/lib/texts/MetaString.cpp index e05af1cc0..60a3375b4 100644 --- a/lib/texts/MetaString.cpp +++ b/lib/texts/MetaString.cpp @@ -11,7 +11,6 @@ #include "MetaString.h" #include "CCreatureHandler.h" -#include "CCreatureSet.h" #include "entities/artifact/CArtifact.h" #include "entities/faction/CFaction.h" #include "entities/hero/CHero.h" @@ -19,6 +18,7 @@ #include "CSkillHandler.h" #include "GameConstants.h" #include "GameLibrary.h" +#include "mapObjects/army/CStackBasicDescriptor.h" #include "mapObjectConstructors/CObjectClassesHandler.h" #include "spells/CSpellHandler.h" #include "serializer/JsonSerializeFormat.h" diff --git a/mapeditor/inspector/armywidget.cpp b/mapeditor/inspector/armywidget.cpp index 174109ead..c44402b2f 100644 --- a/mapeditor/inspector/armywidget.cpp +++ b/mapeditor/inspector/armywidget.cpp @@ -14,6 +14,7 @@ #include "CCreatureHandler.h" #include "../../lib/GameLibrary.h" +#include "../../lib/mapObjects/army/CStackInstance.h" ArmyWidget::ArmyWidget(CArmedInstance & a, QWidget *parent) : QDialog(parent), diff --git a/mapeditor/inspector/armywidget.h b/mapeditor/inspector/armywidget.h index f127f25c8..c3d316b60 100644 --- a/mapeditor/inspector/armywidget.h +++ b/mapeditor/inspector/armywidget.h @@ -12,7 +12,7 @@ #include "../StdInc.h" #include #include "baseinspectoritemdelegate.h" -#include "../lib/mapObjects/CArmedInstance.h" +#include "../lib/mapObjects/army/CArmedInstance.h" const int TOTAL_SLOTS = 7; diff --git a/scripting/lua/api/StackInstance.h b/scripting/lua/api/StackInstance.h index 47e0f5ea1..0b9c12ea6 100644 --- a/scripting/lua/api/StackInstance.h +++ b/scripting/lua/api/StackInstance.h @@ -14,7 +14,7 @@ #include "../LuaWrapper.h" -#include "../../../lib/CCreatureSet.h" +#include "../../../lib/mapObjects/army/CStackInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index e96e826c5..4313222e0 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -25,7 +25,6 @@ #include "../lib/CConfigHandler.h" #include "../lib/CCreatureHandler.h" -#include "../lib/CCreatureSet.h" #include "../lib/CPlayerState.h" #include "../lib/CSoundBase.h" #include "../lib/GameConstants.h" diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index f202e11b7..77108413c 100644 --- a/server/queries/MapQueries.h +++ b/server/queries/MapQueries.h @@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; class CGObjectInstance; class IObjectInterface; +class CArmedInstance; VCMI_LIB_NAMESPACE_END //Created when player starts turn or when player puts game on [ause From 33431adb27a0fc4e2b494ba505c08b991dab2756 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Jul 2025 19:19:17 +0300 Subject: [PATCH 4/5] Apply formatting on changed files --- lib/mapObjects/army/CArmedInstance.cpp | 39 +++++---- lib/mapObjects/army/CArmedInstance.h | 31 ++++--- lib/mapObjects/army/CCommanderInstance.cpp | 21 ++--- lib/mapObjects/army/CCommanderInstance.h | 22 +++-- lib/mapObjects/army/CCreatureSet.cpp | 83 +++++++++---------- lib/mapObjects/army/CCreatureSet.h | 46 ++++++---- lib/mapObjects/army/CSimpleArmy.h | 4 +- lib/mapObjects/army/CStackBasicDescriptor.cpp | 13 +-- lib/mapObjects/army/CStackBasicDescriptor.h | 7 +- lib/mapObjects/army/CStackInstance.cpp | 62 +++++++------- lib/mapObjects/army/CStackInstance.h | 38 +++++---- lib/mapping/CCastleEvent.h | 4 +- lib/mapping/CMapEvent.h | 2 +- lib/mapping/TerrainTile.h | 10 +-- 14 files changed, 203 insertions(+), 179 deletions(-) diff --git a/lib/mapObjects/army/CArmedInstance.cpp b/lib/mapObjects/army/CArmedInstance.cpp index 44c3f60ef..bcd129fde 100644 --- a/lib/mapObjects/army/CArmedInstance.cpp +++ b/lib/mapObjects/army/CArmedInstance.cpp @@ -24,7 +24,7 @@ VCMI_LIB_NAMESPACE_BEGIN void CArmedInstance::randomizeArmy(FactionID type) { - for (auto & elem : stacks) + for(auto & elem : stacks) { if(elem.second->randomStack) { @@ -39,16 +39,16 @@ void CArmedInstance::randomizeArmy(FactionID type) } } -CArmedInstance::CArmedInstance(IGameInfoCallback *cb) - :CArmedInstance(cb, BonusNodeType::ARMY, false) +CArmedInstance::CArmedInstance(IGameInfoCallback * cb) + : CArmedInstance(cb, BonusNodeType::ARMY, false) { } -CArmedInstance::CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic): - CGObjectInstance(cb), - CBonusSystemNode(nodeType, isHypothetic), - nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)), // Take Angelic Alliance troop-mixing freedom of non-evil units into account. - battle(nullptr) +CArmedInstance::CArmedInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic) + : CGObjectInstance(cb) + , CBonusSystemNode(nodeType, isHypothetic) + , nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)) // Take Angelic Alliance troop-mixing freedom of non-evil units into account. + , battle(nullptr) { } @@ -58,7 +58,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() return; auto b = getExportedBonusList().getFirst(Selector::sourceType()(BonusSource::ARMY).And(Selector::type()(BonusType::MORALE))); - if(!b) + if(!b) { b = std::make_shared(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, 0, BonusSourceID()); addNewBonus(b); @@ -70,11 +70,11 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(const auto & slot : Slots()) { - const auto * creature = slot.second->getCreatureID().toEntity(LIBRARY); + const auto * creature = slot.second->getCreatureID().toEntity(LIBRARY); factions.insert(creature->getFactionID()); // Check for undead flag instead of faction (undead mummies are neutral) - if (!hasUndead) + if(!hasUndead) { //this is costly check, let's skip it at first undead hasUndead |= slot.second->hasBonusOfType(BonusType::UNDEAD); @@ -83,16 +83,16 @@ void CArmedInstance::updateMoraleBonusFromArmy() size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account - if (nonEvilAlignmentMix.hasBonus()) + if(nonEvilAlignmentMix.hasBonus()) { size_t mixableFactions = 0; for(auto f : factions) { - if (LIBRARY->factions()->getById(f)->getAlignment() != EAlignment::EVIL) + if(LIBRARY->factions()->getById(f)->getAlignment() != EAlignment::EVIL) mixableFactions++; } - if (mixableFactions > 0) + if(mixableFactions > 0) factionsInArmy -= mixableFactions - 1; } @@ -103,20 +103,20 @@ void CArmedInstance::updateMoraleBonusFromArmy() b->val = +1; bonusDescription.appendTextID("core.arraytxt.115"); //All troops of one alignment +1 } - else if (!factions.empty()) // no bonus from empty garrison + else if(!factions.empty()) // no bonus from empty garrison { b->val = 2 - static_cast(factionsInArmy); bonusDescription.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d bonusDescription.replaceNumber(factionsInArmy); } - + b->description = bonusDescription; nodeHasChanged(); //-1 modifier for any Undead unit in army auto undeadModifier = getExportedBonusList().getFirst(Selector::source(BonusSource::ARMY, BonusCustomSource::undeadMoraleDebuff)); - if(hasUndead) + if(hasUndead) { if(!undeadModifier) { @@ -127,7 +127,6 @@ void CArmedInstance::updateMoraleBonusFromArmy() } else if(undeadModifier) removeBonus(undeadModifier); - } void CArmedInstance::armyChanged() @@ -174,7 +173,7 @@ void CArmedInstance::attachUnitsToArmy() elem.second->setArmy(getArmy()); } -const IBonusBearer* CArmedInstance::getBonusBearer() const +const IBonusBearer * CArmedInstance::getBonusBearer() const { return this; } @@ -187,7 +186,7 @@ void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler) TerrainId CArmedInstance::getCurrentTerrain() const { - if (anchorPos().isValid()) + if(anchorPos().isValid()) return cb->getTile(visitablePos())->getTerrainID(); else return TerrainId::NONE; diff --git a/lib/mapObjects/army/CArmedInstance.h b/lib/mapObjects/army/CArmedInstance.h index e465999bd..640f376aa 100644 --- a/lib/mapObjects/army/CArmedInstance.h +++ b/lib/mapObjects/army/CArmedInstance.h @@ -13,8 +13,8 @@ #include "../CGObjectInstance.h" -#include "../../bonuses/CBonusSystemNode.h" #include "../../bonuses/BonusCache.h" +#include "../../bonuses/CBonusSystemNode.h" #include @@ -36,26 +36,32 @@ protected: virtual CBonusSystemNode & whatShouldBeAttached(); public: - BattleInfo *battle; //set to the current battle, if engaged + BattleInfo * battle; //set to the current battle, if engaged void randomizeArmy(FactionID type); virtual void updateMoraleBonusFromArmy(); void armyChanged() override; - CArmedInstance * getArmy() final { return this; } - const CArmedInstance * getArmy() const final { return this; } + CArmedInstance * getArmy() final + { + return this; + } + const CArmedInstance * getArmy() const final + { + return this; + } ////////////////////////////////////////////////////////////////////////// //IConstBonusProvider - const IBonusBearer* getBonusBearer() const override; + const IBonusBearer * getBonusBearer() const override; void attachToBonusSystem(CGameState & gs) override; void detachFromBonusSystem(CGameState & gs) override; void restoreBonusSystem(CGameState & gs) override; ////////////////////////////////////////////////////////////////////////// - CArmedInstance(IGameInfoCallback *cb); - CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic); + CArmedInstance(IGameInfoCallback * cb); + CArmedInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic); PlayerColor getOwner() const override { @@ -63,14 +69,15 @@ public: } TerrainId getCurrentTerrain() const; - + void serializeJsonOptions(JsonSerializeFormat & handler) override; - template void serialize(Handler &h) + template + void serialize(Handler & h) { - h & static_cast(*this); - h & static_cast(*this); - h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); if(!h.saving && h.loadingGamestate) attachUnitsToArmy(); diff --git a/lib/mapObjects/army/CCommanderInstance.cpp b/lib/mapObjects/army/CCommanderInstance.cpp index ad7298e78..654ab7730 100644 --- a/lib/mapObjects/army/CCommanderInstance.cpp +++ b/lib/mapObjects/army/CCommanderInstance.cpp @@ -15,11 +15,12 @@ VCMI_LIB_NAMESPACE_BEGIN -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb) - :CStackInstance(cb) -{} +CCommanderInstance::CCommanderInstance(IGameInfoCallback * cb) + : CStackInstance(cb) +{ +} -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id) +CCommanderInstance::CCommanderInstance(IGameInfoCallback * cb, const CreatureID & id) : CStackInstance(cb, BonusNodeType::COMMANDER, false) , name("Commando") { @@ -27,16 +28,16 @@ CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & level = 1; setCount(1); setType(nullptr); - secondarySkills.resize (ECommander::SPELL_POWER + 1); + secondarySkills.resize(ECommander::SPELL_POWER + 1); setType(id); //TODO - parse them } -void CCommanderInstance::setAlive (bool Alive) +void CCommanderInstance::setAlive(bool Alive) { //TODO: helm of immortality alive = Alive; - if (!alive) + if(!alive) { removeBonusesRecursive(Bonus::UntilCommanderKilled); } @@ -49,15 +50,15 @@ bool CCommanderInstance::canGainExperience() const int CCommanderInstance::getExpRank() const { - return LIBRARY->heroh->level (getTotalExperience()); + return LIBRARY->heroh->level(getTotalExperience()); } int CCommanderInstance::getLevel() const { - return std::max (1, getExpRank()); + return std::max(1, getExpRank()); } -void CCommanderInstance::levelUp () +void CCommanderInstance::levelUp() { level++; for(const auto & bonus : LIBRARY->creh->commanderLevelPremy) diff --git a/lib/mapObjects/army/CCommanderInstance.h b/lib/mapObjects/army/CCommanderInstance.h index 054071d61..f0984de8f 100644 --- a/lib/mapObjects/army/CCommanderInstance.h +++ b/lib/mapObjects/army/CCommanderInstance.h @@ -22,24 +22,28 @@ public: ui8 alive; //maybe change to bool when breaking save compatibility? ui8 level; //required only to count callbacks std::string name; // each Commander has different name - std::vector secondarySkills; //ID -> level - std::set specialSkills; + std::vector secondarySkills; //ID -> level + std::set specialSkills; //std::vector arts; - CCommanderInstance(IGameInfoCallback *cb); - CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id); - void setAlive (bool alive); - void levelUp (); + CCommanderInstance(IGameInfoCallback * cb); + CCommanderInstance(IGameInfoCallback * cb, const CreatureID & id); + void setAlive(bool alive); + void levelUp(); bool canGainExperience() const override; bool gainsLevel() const; //true if commander has lower level than should upon his experience - ui64 getPower() const override {return 0;}; + ui64 getPower() const override + { + return 0; + }; int getExpRank() const override; int getLevel() const override; ArtBearer bearerType() const override; //from CArtifactSet - template void serialize(Handler &h) + template + void serialize(Handler & h) { - h & static_cast(*this); + h & static_cast(*this); h & alive; h & level; h & name; diff --git a/lib/mapObjects/army/CCreatureSet.cpp b/lib/mapObjects/army/CCreatureSet.cpp index 78f4fa90e..2e0683cc4 100644 --- a/lib/mapObjects/army/CCreatureSet.cpp +++ b/lib/mapObjects/army/CCreatureSet.cpp @@ -18,8 +18,7 @@ VCMI_LIB_NAMESPACE_BEGIN - -bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs) + bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs) { return lhs.first->getAIValue() < rhs.first->getAIValue(); // Descendant order sorting } @@ -27,7 +26,7 @@ bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPair const CStackInstance & CCreatureSet::operator[](const SlotID & slot) const { auto i = stacks.find(slot); - if (i != stacks.end()) + if(i != stacks.end()) return *i->second; else throw std::runtime_error("That slot is empty!"); @@ -36,7 +35,7 @@ const CStackInstance & CCreatureSet::operator[](const SlotID & slot) const const CCreature * CCreatureSet::getCreature(const SlotID & slot) const { auto i = stacks.find(slot); - if (i != stacks.end()) + if(i != stacks.end()) return i->second->getCreature(); else return nullptr; @@ -71,7 +70,7 @@ SlotID CCreatureSet::getSlotFor(const CreatureID & creature, ui32 slotsAmount) c return getSlotFor(creature.toCreature(), slotsAmount); } -SlotID CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount) const +SlotID CCreatureSet::getSlotFor(const CCreature * c, ui32 slotsAmount) const { assert(c); for(const auto & elem : stacks) @@ -138,7 +137,6 @@ bool CCreatureSet::isCreatureBalanced(const CCreature * c, TQuantity ignoreAmoun if(count == ignoreAmount || count < 1) continue; - if(count > max) max = count; if(count < min) @@ -151,7 +149,7 @@ bool CCreatureSet::isCreatureBalanced(const CCreature * c, TQuantity ignoreAmoun SlotID CCreatureSet::getFreeSlot(ui32 slotsAmount) const { - for(ui32 i=0; i CCreatureSet::getFreeSlotsQueue(ui32 slotsAmount) const { std::queue freeSlots; - for (ui32 i = 0; i < slotsAmount; i++) + for(ui32 i = 0; i < slotsAmount; i++) { auto slot = SlotID(i); @@ -225,7 +223,7 @@ TCreatureQueue CCreatureSet::getCreatureQueue(const SlotID & exclude) const TQuantity CCreatureSet::getStackCount(const SlotID & slot) const { - if (!hasStackAtSlot(slot)) + if(!hasStackAtSlot(slot)) return 0; return stacks.at(slot)->getCount(); } @@ -243,9 +241,9 @@ TExpType CCreatureSet::getStackAverageExperience(const SlotID & slot) const bool CCreatureSet::mergeableStacks(std::pair & out, const SlotID & preferable) const /*looks for two same stacks, returns slot positions */ { //try to match creature to our preferred stack - if(preferable.validSlot() && vstd::contains(stacks, preferable)) + if(preferable.validSlot() && vstd::contains(stacks, preferable)) { - const CCreature *cr = stacks.find(preferable)->second->getCreature(); + const CCreature * cr = stacks.find(preferable)->second->getCreature(); for(const auto & elem : stacks) { if(cr == elem.second->getType() && elem.first != preferable) @@ -274,7 +272,7 @@ bool CCreatureSet::mergeableStacks(std::pair & out, const SlotID void CCreatureSet::addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging) { - const CCreature *c = cre.toCreature(); + const CCreature * c = cre.toCreature(); if(!hasStackAtSlot(slot)) { @@ -331,18 +329,18 @@ bool CCreatureSet::needsLastStack() const ui64 CCreatureSet::getArmyStrength(int fortLevel) const { ui64 ret = 0; - for (const auto& elem : stacks) + for(const auto & elem : stacks) { ui64 powerToAdd = elem.second->getPower(); - if (fortLevel > 0) + if(fortLevel > 0) { - if (!elem.second->hasBonusOfType(BonusType::FLYING)) + if(!elem.second->hasBonusOfType(BonusType::FLYING)) { powerToAdd /= fortLevel; - if (!elem.second->hasBonusOfType(BonusType::SHOOTER)) + if(!elem.second->hasBonusOfType(BonusType::SHOOTER)) powerToAdd /= fortLevel; } - } + } ret += powerToAdd; } return ret; @@ -351,7 +349,7 @@ ui64 CCreatureSet::getArmyStrength(int fortLevel) const ui64 CCreatureSet::getArmyCost() const { ui64 ret = 0; - for (const auto& elem : stacks) + for(const auto & elem : stacks) ret += elem.second->getMarketValue(); return ret; } @@ -372,7 +370,7 @@ std::string CCreatureSet::getRoughAmount(const SlotID & slot, int mode) const if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) return CCreature::getQuantityRangeStringForId(quantity); - return LIBRARY->generaltexth->arraytxt[(174 + mode) + 3*(int)quantity]; + return LIBRARY->generaltexth->arraytxt[(174 + mode) + 3 * (int)quantity]; } return ""; } @@ -450,7 +448,8 @@ CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const { if(hasStackAtSlot(slot)) return stacks.find(slot)->second.get(); - else return nullptr; + else + return nullptr; } void CCreatureSet::eraseStack(const SlotID & slot) @@ -459,7 +458,7 @@ void CCreatureSet::eraseStack(const SlotID & slot) detachStack(slot); } -bool CCreatureSet::contains(const CStackInstance *stack) const +bool CCreatureSet::contains(const CStackInstance * stack) const { if(!stack) return false; @@ -471,10 +470,10 @@ bool CCreatureSet::contains(const CStackInstance *stack) const return false; } -SlotID CCreatureSet::findStack(const CStackInstance *stack) const +SlotID CCreatureSet::findStack(const CStackInstance * stack) const { const auto * h = dynamic_cast(this); - if (h && h->getCommander() == stack) + if(h && h->getCommander() == stack) return SlotID::COMMANDER_SLOT_PLACEHOLDER; if(!stack) @@ -499,7 +498,7 @@ void CCreatureSet::putStack(const SlotID & slot, std::unique_ptr void CCreatureSet::joinStack(const SlotID & slot, std::unique_ptr stack) { - [[maybe_unused]] const CCreature *c = getCreature(slot); + [[maybe_unused]] const CCreature * c = getCreature(slot); assert(c == stack->getType()); assert(c); @@ -530,7 +529,7 @@ void CCreatureSet::changeStackCount(const SlotID & slot, TQuantity toAdd) CCreatureSet::~CCreatureSet() = default; -void CCreatureSet::setToArmy(CSimpleArmy &src) +void CCreatureSet::setToArmy(CSimpleArmy & src) { clearSlots(); while(src) @@ -566,12 +565,12 @@ void CCreatureSet::setStackType(const SlotID & slot, const CreatureID & type) armyChanged(); } -bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const +bool CCreatureSet::canBeMergedWith(const CCreatureSet & cs, bool allowMergingStacks) const { if(!allowMergingStacks) { int freeSlots = stacksCount() - GameConstants::ARMY_SIZE; - std::set cresToAdd; + std::set cresToAdd; for(const auto & elem : cs.stacks) { SlotID dest = getSlotFor(elem.second->getCreature()); @@ -587,13 +586,13 @@ bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStac //get types of creatures that need their own slot for(const auto & elem : cs.stacks) - if ((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) - cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible - //cres.addToSlot(elem.first, elem.second->type->getId(), 1, true); + if((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) + cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible + //cres.addToSlot(elem.first, elem.second->type->getId(), 1, true); for(const auto & elem : stacks) { - if ((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) - cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible + if((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) + cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible else return false; //no place found } @@ -611,22 +610,22 @@ bool CCreatureSet::hasUnits(const std::vector & units, bo for(const auto & slot : Slots()) { const auto & heroStack = slot.second; - if (heroStack->getType() == reqStack.getType()) + if(heroStack->getType() == reqStack.getType()) { count += heroStack->getCount(); testedSlots += 1; } } - if (count > reqStack.getCount()) + if(count > reqStack.getCount()) foundExtraCreatures = true; - if (count < reqStack.getCount()) //not enough creatures of this kind + if(count < reqStack.getCount()) //not enough creatures of this kind return false; } - if (requireLastStack) + if(requireLastStack) { - if (!foundExtraCreatures && testedSlots >= Slots().size()) + if(!foundExtraCreatures && testedSlots >= Slots().size()) return false; } @@ -638,16 +637,13 @@ bool CCreatureSet::hasStackAtSlot(const SlotID & slot) const return vstd::contains(stacks, slot); } -CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs) +CCreatureSet & CCreatureSet::operator=(const CCreatureSet & cs) { assert(0); return *this; } -void CCreatureSet::armyChanged() -{ - -} +void CCreatureSet::armyChanged() {} void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize) { @@ -657,13 +653,12 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin handler.serializeEnum("formation", formation, NArmyFormation::names); auto a = handler.enterArray(armyFieldName); - if(handler.saving) { size_t sz = 0; for(const auto & p : stacks) - vstd::amax(sz, p.first.getNum()+1); + vstd::amax(sz, p.first.getNum() + 1); if(fixedSize) vstd::amax(sz, fixedSize.value()); diff --git a/lib/mapObjects/army/CCreatureSet.h b/lib/mapObjects/army/CCreatureSet.h index 67274238f..933e6fc14 100644 --- a/lib/mapObjects/army/CCreatureSet.h +++ b/lib/mapObjects/army/CCreatureSet.h @@ -34,13 +34,13 @@ using TCreatureQueue = std::priority_queue names{ "wide", "tight" }; +static const std::vector names{"wide", "tight"}; } class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable //seven combined creatures { CCreatureSet(const CCreatureSet &) = delete; - CCreatureSet &operator=(const CCreatureSet&); + CCreatureSet & operator=(const CCreatureSet &); public: TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity) @@ -54,17 +54,23 @@ public: const TSlots &Slots() const {return stacks;} - void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature - void addToSlot(const SlotID & slot, std::unique_ptr stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature + //Adds stack to slot. Slot must be empty or with same type creature + void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); + //Adds stack to slot. Slot must be empty or with same type creature + void addToSlot(const SlotID & slot, std::unique_ptr stack, bool allowMerging = true); + void clearSlots() override; void setFormation(EArmyFormation tight); virtual CArmedInstance * getArmy() { return nullptr; } virtual const CArmedInstance * getArmy() const { return nullptr; } - //basic operations - void putStack(const SlotID & slot, std::unique_ptr stack); //adds new stack to the army, slot must be empty - void setStackCount(const SlotID & slot, TQuantity count); //stack must exist! - std::unique_ptr detachStack(const SlotID & slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted) + //adds new stack to the army, slot must be empty + void putStack(const SlotID & slot, std::unique_ptr stack); + //stack must exist! + void setStackCount(const SlotID & slot, TQuantity count); + //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted) + std::unique_ptr detachStack(const SlotID & slot); + void setStackType(const SlotID & slot, const CreatureID & type); /// Give specified amount of experience to all units in army @@ -86,9 +92,14 @@ public: /// Slot must be non-empty and contain more units that split quantity std::unique_ptr splitStack(const SlotID & slot, TQuantity toSplit); - void changeStackCount(const SlotID & slot, TQuantity toAdd); //stack must exist! - bool setCreature (SlotID slot, CreatureID type, TQuantity quantity) override; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack - void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all. + //stack must exist! + void changeStackCount(const SlotID & slot, TQuantity toAdd); + + //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack + bool setCreature(SlotID slot, CreatureID type, TQuantity quantity) override; + + //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all. + void setToArmy(CSimpleArmy & src); const CStackInstance & getStack(const SlotID & slot) const; //stack must exist CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr @@ -96,12 +107,12 @@ public: int getStackCount(const SlotID & slot) const; TExpType getStackTotalExperience(const SlotID & slot) const; TExpType getStackAverageExperience(const SlotID & slot) const; - SlotID findStack(const CStackInstance *stack) const; //-1 if none + SlotID findStack(const CStackInstance * stack) const; //-1 if none SlotID getSlotFor(const CreatureID & creature, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available - SlotID getSlotFor(const CCreature *c, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available + SlotID getSlotFor(const CCreature * c, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available bool hasCreatureSlots(const CCreature * c, const SlotID & exclude) const; std::vector getCreatureSlots(const CCreature * c, const SlotID & exclude, TQuantity ignoreAmount = -1) const; - bool isCreatureBalanced(const CCreature* c, TQuantity ignoreAmount = 1) const; // Check if the creature is evenly distributed across slots + bool isCreatureBalanced(const CCreature * c, TQuantity ignoreAmount = 1) const; // Check if the creature is evenly distributed across slots SlotID getFreeSlot(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns first free slot std::vector getFreeSlots(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; @@ -122,15 +133,16 @@ public: std::string getArmyDescription() const; bool hasStackAtSlot(const SlotID & slot) const; - bool contains(const CStackInstance *stack) const; - bool canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks = true) const; + bool contains(const CStackInstance * stack) const; + bool canBeMergedWith(const CCreatureSet & cs, bool allowMergingStacks = true) const; /// Returns true if this creature set contains all listed units /// If requireLastStack is true, then this function will also /// require presence of any unit other than requested (or more units than requested) bool hasUnits(const std::vector & units, bool requireLastStack = true) const; - template void serialize(Handler &h) + template + void serialize(Handler & h) { h & stacks; h & formation; diff --git a/lib/mapObjects/army/CSimpleArmy.h b/lib/mapObjects/army/CSimpleArmy.h index 8fb57f695..8b4888c95 100644 --- a/lib/mapObjects/army/CSimpleArmy.h +++ b/lib/mapObjects/army/CSimpleArmy.h @@ -44,8 +44,8 @@ public: return !army.empty(); } - - template void serialize(Handler &h) + template + void serialize(Handler & h) { h & army; } diff --git a/lib/mapObjects/army/CStackBasicDescriptor.cpp b/lib/mapObjects/army/CStackBasicDescriptor.cpp index 15d19aba2..5370c1aac 100644 --- a/lib/mapObjects/army/CStackBasicDescriptor.cpp +++ b/lib/mapObjects/army/CStackBasicDescriptor.cpp @@ -19,14 +19,15 @@ VCMI_LIB_NAMESPACE_BEGIN //This constructor should be placed here to avoid side effects CStackBasicDescriptor::CStackBasicDescriptor() = default; -CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count): - typeID(id), - count(Count) +CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count) + : typeID(id) + , count(Count) { } -CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count) - : typeID(c ? c->getId() : CreatureID()), count(Count) +CStackBasicDescriptor::CStackBasicDescriptor(const CCreature * c, TQuantity Count) + : typeID(c ? c->getId() : CreatureID()) + , count(Count) { } @@ -61,7 +62,7 @@ void CStackBasicDescriptor::setCount(TQuantity newCount) count = newCount; } -bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) +bool operator==(const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) { return l.typeID == r.typeID && l.count == r.count; } diff --git a/lib/mapObjects/army/CStackBasicDescriptor.h b/lib/mapObjects/army/CStackBasicDescriptor.h index a89d5ff70..613ba8b9a 100644 --- a/lib/mapObjects/army/CStackBasicDescriptor.h +++ b/lib/mapObjects/army/CStackBasicDescriptor.h @@ -28,7 +28,7 @@ class DLL_LINKAGE CStackBasicDescriptor public: CStackBasicDescriptor(); CStackBasicDescriptor(const CreatureID & id, TQuantity Count); - CStackBasicDescriptor(const CCreature *c, TQuantity Count); + CStackBasicDescriptor(const CCreature * c, TQuantity Count); virtual ~CStackBasicDescriptor() = default; const Creature * getType() const; @@ -39,9 +39,10 @@ public: virtual void setType(const CCreature * c); virtual void setCount(TQuantity amount); - friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); + friend bool operator==(const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); - template void serialize(Handler &h) + template + void serialize(Handler & h) { if(h.saving) { diff --git a/lib/mapObjects/army/CStackInstance.cpp b/lib/mapObjects/army/CStackInstance.cpp index c964a9311..11f347e5c 100644 --- a/lib/mapObjects/army/CStackInstance.cpp +++ b/lib/mapObjects/army/CStackInstance.cpp @@ -23,11 +23,12 @@ VCMI_LIB_NAMESPACE_BEGIN -CStackInstance::CStackInstance(IGameInfoCallback *cb) +CStackInstance::CStackInstance(IGameInfoCallback * cb) : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) -{} +{ +} -CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic) +CStackInstance::CStackInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic) : CBonusSystemNode(nodeType, isHypothetic) , CStackBasicDescriptor(nullptr, 0) , CArtifactSet(cb) @@ -35,9 +36,10 @@ CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bo , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE)) , initiative(this, Selector::type()(BonusType::STACKS_SPEED)) , totalExperience(0) -{} +{ +} -CStackInstance::CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity Count, bool isHypothetic) +CStackInstance::CStackInstance(IGameInfoCallback * cb, const CreatureID & id, TQuantity Count, bool isHypothetic) : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) { setType(id); @@ -51,14 +53,14 @@ CCreature::CreatureQuantityId CStackInstance::getQuantityID() const int CStackInstance::getExpRank() const { - if (!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + if(!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) return 0; int tier = getType()->getLevel(); - if (vstd::iswithin(tier, 1, 7)) + if(vstd::iswithin(tier, 1, 7)) { for(int i = static_cast(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic! { //exp values vary from 1st level to max exp at 11th level - if (getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) + if(getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) return ++i; //faster, but confusing - 0 index mean 1st level of experience } return 0; @@ -67,7 +69,7 @@ int CStackInstance::getExpRank() const { for(int i = static_cast(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i) { - if (getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) + if(getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) return ++i; } return 0; @@ -81,25 +83,25 @@ int CStackInstance::getLevel() const void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit) { - if (!canGainExperience()) + if(!canGainExperience()) return; int level = std::clamp(getLevel(), 1, 7); TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back(); - TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level]/100); + TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level] / 100); TExpType maxExperience = maxAmountPerUnit * getCount(); TExpType maxExperienceToGain = maxExperience - totalExperience; TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount()); - totalExperience += actualGainedExperience; + totalExperience += actualGainedExperience; } void CStackInstance::giveTotalStackExperience(TExpType experienceToGive) { - if (!canGainExperience()) + if(!canGainExperience()) return; - totalExperience += experienceToGive; + totalExperience += experienceToGive; } TExpType CStackInstance::getTotalExperience() const @@ -119,18 +121,18 @@ bool CStackInstance::canGainExperience() const void CStackInstance::setType(const CreatureID & creID) { - if (creID == CreatureID::NONE) - setType(nullptr);//FIXME: unused branch? + if(creID == CreatureID::NONE) + setType(nullptr); //FIXME: unused branch? else setType(creID.toCreature()); } -void CStackInstance::setType(const CCreature *c) +void CStackInstance::setType(const CCreature * c) { if(getCreature()) { detachFromSource(*getCreature()); - if (LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + if(LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100; } @@ -144,7 +146,7 @@ void CStackInstance::setCount(TQuantity newCount) { assert(newCount >= 0); - if (newCount < getCount()) + if(newCount < getCount()) { TExpType averageExperience = totalExperience / getCount(); totalExperience = averageExperience * newCount; @@ -154,9 +156,9 @@ void CStackInstance::setCount(TQuantity newCount) nodeHasChanged(); } -std::string CStackInstance::bonusToString(const std::shared_ptr& bonus) const +std::string CStackInstance::bonusToString(const std::shared_ptr & bonus) const { - if (!bonus->description.empty()) + if(!bonus->description.empty()) return bonus->description.toString(); else return LIBRARY->getBth()->bonusToString(bonus, this); @@ -164,7 +166,7 @@ std::string CStackInstance::bonusToString(const std::shared_ptr& bonus) c ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr & bonus) const { - if (!bonus->customIconPath.empty()) + if(!bonus->customIconPath.empty()) return bonus->customIconPath; return LIBRARY->getBth()->bonusToGraphics(bonus); } @@ -191,7 +193,7 @@ void CStackInstance::setArmy(CArmedInstance * ArmyObj) if(ArmyObj) { - attachTo(const_cast(*ArmyObj)); + attachTo(const_cast(*ArmyObj)); armyInstance = ArmyObj; } } @@ -200,12 +202,12 @@ std::string CStackInstance::getQuantityTXT(bool capitalized) const { CCreature::CreatureQuantityId quantity = getQuantityID(); - if ((int)quantity) + if((int)quantity) { if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) return CCreature::getQuantityRangeStringForId(quantity); - return LIBRARY->generaltexth->arraytxt[174 + (int)quantity*3 - 1 - capitalized]; + return LIBRARY->generaltexth->arraytxt[174 + (int)quantity * 3 - 1 - capitalized]; } else return ""; @@ -241,7 +243,7 @@ PlayerColor CStackInstance::getOwner() const int32_t CStackInstance::getInitiative(int turn) const { - if (turn == 0) + if(turn == 0) return initiative.getValue(); return ACreature::getInitiative(turn); @@ -249,7 +251,7 @@ int32_t CStackInstance::getInitiative(int turn) const TerrainId CStackInstance::getNativeTerrain() const { - if (nativeTerrain.hasBonus()) + if(nativeTerrain.hasBonus()) return TerrainId::ANY_TERRAIN; return getFactionID().toEntity(LIBRARY)->getNativeTerrain(); @@ -311,7 +313,7 @@ void CStackInstance::removeArtifact(const ArtifactPosition & pos) void CStackInstance::serializeJson(JsonSerializeFormat & handler) { //todo: artifacts - CStackBasicDescriptor::serializeJson(handler);//must be first + CStackBasicDescriptor::serializeJson(handler); //must be first if(handler.saving) { @@ -335,7 +337,7 @@ void CStackInstance::serializeJson(JsonSerializeFormat & handler) handler.serializeInt("level", level, 0); handler.serializeInt("upgrade", upgrade, 0); - randomStack = RandomStackInfo{ level, upgrade }; + randomStack = RandomStackInfo{level, upgrade}; } } } @@ -348,7 +350,7 @@ FactionID CStackInstance::getFactionID() const return FactionID::NEUTRAL; } -const IBonusBearer* CStackInstance::getBonusBearer() const +const IBonusBearer * CStackInstance::getBonusBearer() const { return this; } diff --git a/lib/mapObjects/army/CStackInstance.h b/lib/mapObjects/army/CStackInstance.h index 7c84a8d2c..8e9a93163 100644 --- a/lib/mapObjects/army/CStackInstance.h +++ b/lib/mapObjects/army/CStackInstance.h @@ -15,8 +15,8 @@ #include "bonuses/BonusCache.h" #include "bonuses/CBonusSystemNode.h" #include "callback/GameCallbackHolder.h" -#include "mapObjects/CGObjectInstance.h" #include "entities/artifact/CArtifactSet.h" +#include "mapObjects/CGObjectInstance.h" VCMI_LIB_NAMESPACE_BEGIN @@ -34,9 +34,12 @@ class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDe CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object - IGameInfoCallback * getCallback() const final { return cb; } + IGameInfoCallback * getCallback() const final + { + return cb; + } - TExpType totalExperience;//commander needs same amount of exp as hero + TExpType totalExperience; //commander needs same amount of exp as hero public: struct RandomStackInfo { @@ -48,23 +51,24 @@ public: CArmedInstance * getArmy(); const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object - void setArmy(CArmedInstance *ArmyObj); + void setArmy(CArmedInstance * ArmyObj); TExpType getTotalExperience() const; TExpType getAverageExperience() const; virtual bool canGainExperience() const; - template void serialize(Handler &h) + template + void serialize(Handler & h) { - h & static_cast(*this); - h & static_cast(*this); - h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); - if (h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) + if(h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) { // no-op } - if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + if(h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) { ObjectInstanceID dummyID; h & dummyID; @@ -76,7 +80,7 @@ public: } h & totalExperience; - if (!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) + if(!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) { totalExperience *= getCount(); } @@ -85,11 +89,11 @@ public: void serializeJson(JsonSerializeFormat & handler); //overrides CBonusSystemNode - std::string bonusToString(const std::shared_ptr& bonus) const override; // how would bonus description look for this particular type of node + std::string bonusToString(const std::shared_ptr & bonus) const override; // how would bonus description look for this particular type of node ImagePath bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others //IConstBonusProvider - const IBonusBearer* getBonusBearer() const override; + const IBonusBearer * getBonusBearer() const override; //INativeTerrainProvider FactionID getFactionID() const override; @@ -102,9 +106,9 @@ public: virtual int getLevel() const; //different for regular stack and commander CreatureID getCreatureID() const; //-1 if not available std::string getName() const; //plural or singular - CStackInstance(IGameInfoCallback *cb); - CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic = false); - CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); + CStackInstance(IGameInfoCallback * cb); + CStackInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic = false); + CStackInstance(IGameInfoCallback * cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); virtual ~CStackInstance() = default; void setType(const CreatureID & creID); @@ -116,7 +120,7 @@ public: void giveTotalStackExperience(TExpType exp); bool valid(bool allowUnrandomized) const; - ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet + ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override; //from CArtifactSet void removeArtifact(const ArtifactPosition & pos) override; ArtBearer bearerType() const override; //from CArtifactSet std::string nodeName() const override; //from CBonusSystemnode diff --git a/lib/mapping/CCastleEvent.h b/lib/mapping/CCastleEvent.h index 86f32ae1c..cc88a05de 100644 --- a/lib/mapping/CCastleEvent.h +++ b/lib/mapping/CCastleEvent.h @@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN /// The castle event builds/adds buildings/creatures for a specific town. -class DLL_LINKAGE CCastleEvent: public CMapEvent +class DLL_LINKAGE CCastleEvent : public CMapEvent { public: CCastleEvent() = default; @@ -23,7 +23,7 @@ public: std::set buildings; std::vector creatures; - template + template void serialize(Handler & h) { h & static_cast(*this); diff --git a/lib/mapping/CMapEvent.h b/lib/mapping/CMapEvent.h index a3c496077..6e9aff4ab 100644 --- a/lib/mapping/CMapEvent.h +++ b/lib/mapping/CMapEvent.h @@ -39,7 +39,7 @@ public: std::vector deletedObjectsInstances; - template + template void serialize(Handler & h) { h & name; diff --git a/lib/mapping/TerrainTile.h b/lib/mapping/TerrainTile.h index 25f328eb2..092abb0ee 100644 --- a/lib/mapping/TerrainTile.h +++ b/lib/mapping/TerrainTile.h @@ -67,7 +67,7 @@ struct DLL_LINKAGE TerrainTile std::vector visitableObjects; std::vector blockingObjects; - template + template void serialize(Handler & h) { h & terrainType; @@ -78,7 +78,7 @@ struct DLL_LINKAGE TerrainTile h & roadDir; h & extTileFlags; - if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + if(h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) { h & visitableObjects; h & blockingObjects; @@ -87,14 +87,12 @@ struct DLL_LINKAGE TerrainTile { std::vector> objectPtrs; h & objectPtrs; - for (const auto & ptr : objectPtrs) + for(const auto & ptr : objectPtrs) visitableObjects.push_back(ptr->id); h & objectPtrs; - for (const auto & ptr : objectPtrs) + for(const auto & ptr : objectPtrs) blockingObjects.push_back(ptr->id); } - - } }; From 4c2d67eb4561bb2e22aec7096695ab2f9acf9efc Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Jul 2025 19:50:52 +0300 Subject: [PATCH 5/5] Fix build --- lib/mapObjects/army/CCreatureSet.cpp | 6 ------ lib/mapObjects/army/CCreatureSet.h | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/mapObjects/army/CCreatureSet.cpp b/lib/mapObjects/army/CCreatureSet.cpp index 2e0683cc4..1d9bf3fa6 100644 --- a/lib/mapObjects/army/CCreatureSet.cpp +++ b/lib/mapObjects/army/CCreatureSet.cpp @@ -637,12 +637,6 @@ bool CCreatureSet::hasStackAtSlot(const SlotID & slot) const return vstd::contains(stacks, slot); } -CCreatureSet & CCreatureSet::operator=(const CCreatureSet & cs) -{ - assert(0); - return *this; -} - void CCreatureSet::armyChanged() {} void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize) diff --git a/lib/mapObjects/army/CCreatureSet.h b/lib/mapObjects/army/CCreatureSet.h index 933e6fc14..d208a52b1 100644 --- a/lib/mapObjects/army/CCreatureSet.h +++ b/lib/mapObjects/army/CCreatureSet.h @@ -10,6 +10,7 @@ #pragma once #include "CSimpleArmy.h" +#include "CStackInstance.h" #include "serializer/Serializeable.h" @@ -37,10 +38,10 @@ namespace NArmyFormation static const std::vector names{"wide", "tight"}; } -class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable //seven combined creatures +class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable, boost::noncopyable //seven combined creatures { CCreatureSet(const CCreatureSet &) = delete; - CCreatureSet & operator=(const CCreatureSet &); + CCreatureSet & operator=(const CCreatureSet &) = delete; public: TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity)