diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index d663a1bf4..5e3f4b022 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -26,7 +26,7 @@ void DamageCache::cacheDamage(const battle::Unit * attacker, const battle::Unit } -void DamageCache::buildDamageCache(std::shared_ptr hb, int side) +void DamageCache::buildDamageCache(std::shared_ptr hb, BattleSide side) { auto stacks = hb->battleGetUnitsIf([=](const battle::Unit * u) -> bool { @@ -245,7 +245,7 @@ AttackPossibility AttackPossibility::evaluate( std::vector defenderHex; if(attackInfo.shooting) - defenderHex = defender->getHexes(); + defenderHex.push_back(defender->getPosition()); else defenderHex = CStack::meleeAttackHexes(attacker, defender, hex); diff --git a/AI/BattleAI/AttackPossibility.h b/AI/BattleAI/AttackPossibility.h index 014a4266a..990dcdb00 100644 --- a/AI/BattleAI/AttackPossibility.h +++ b/AI/BattleAI/AttackPossibility.h @@ -27,7 +27,7 @@ public: void cacheDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr hb); int64_t getDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr hb); int64_t getOriginalDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr hb); - void buildDamageCache(std::shared_ptr hb, int side); + void buildDamageCache(std::shared_ptr hb, BattleSide side); }; /// diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 4e3e97787..202681bf7 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -32,7 +32,7 @@ #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl)) CBattleAI::CBattleAI() - : side(-1), + : side(BattleSide::NONE), wasWaitingForRealize(false), wasUnlockingGs(false) { @@ -100,7 +100,7 @@ void CBattleAI::yourTacticPhase(const BattleID & battleID, int distance) cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide())); } -static float getStrengthRatio(std::shared_ptr cb, int side) +static float getStrengthRatio(std::shared_ptr cb, BattleSide side) { auto stacks = cb->battleGetAllStacks(); auto our = 0; @@ -243,7 +243,7 @@ BattleAction CBattleAI::useCatapult(const BattleID & battleID, const CStack * st return attack; } -void CBattleAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed) +void CBattleAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide Side, bool replayAllowed) { LOG_TRACE(logAi); side = Side; diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 7074667ef..f36c2cb26 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -27,7 +27,7 @@ struct CurrentOffensivePotential std::map ourAttacks; std::map enemyAttacks; - CurrentOffensivePotential(ui8 side) + CurrentOffensivePotential(BattleSide side) { for(auto stack : cbc->battleGetStacks()) { @@ -54,7 +54,7 @@ struct CurrentOffensivePotential class CBattleAI : public CBattleGameInterface { - int side; + BattleSide side; std::shared_ptr cb; std::shared_ptr env; @@ -80,7 +80,7 @@ public: BattleAction useCatapult(const BattleID & battleID, const CStack *stack); BattleAction useHealingTent(const BattleID & battleID, const CStack *stack); - void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool Side, bool replayAllowed) override; + void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override; //void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero //void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero //void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack @@ -93,7 +93,7 @@ public: //void battleSpellCast(const BattleSpellCast *sc) override; //void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks //void battleTriggerEffect(const BattleTriggerEffect & bte) override; - //void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right + //void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side) override; //called by engine when battle starts; side=0 - left, side=1 - right //void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack AutocombatPreferences autobattlePreferences = AutocombatPreferences(); }; diff --git a/AI/BattleAI/BattleEvaluator.cpp b/AI/BattleAI/BattleEvaluator.cpp index a0f0c01af..a20899f11 100644 --- a/AI/BattleAI/BattleEvaluator.cpp +++ b/AI/BattleAI/BattleEvaluator.cpp @@ -686,7 +686,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack) spellcast.spell = castToPerform.spell->id; spellcast.setTarget(castToPerform.dest); spellcast.side = side; - spellcast.stackNumber = (!side) ? -1 : -2; + spellcast.stackNumber = -1; cb->battleMakeSpellAction(battleID, spellcast); activeActionMade = true; diff --git a/AI/BattleAI/BattleEvaluator.h b/AI/BattleAI/BattleEvaluator.h index 6198d56a4..1c71c45f6 100644 --- a/AI/BattleAI/BattleEvaluator.h +++ b/AI/BattleAI/BattleEvaluator.h @@ -33,7 +33,7 @@ class BattleEvaluator std::optional cachedAttack; PlayerColor playerID; BattleID battleID; - int side; + BattleSide side; float cachedScore; DamageCache damageCache; float strengthRatio; @@ -54,7 +54,7 @@ public: const battle::Unit * activeStack, PlayerColor playerID, BattleID battleID, - int side, + BattleSide side, float strengthRatio) :scoreEvaluator(cb->getBattle(battleID), env, strengthRatio), cachedAttack(), playerID(playerID), side(side), env(env), cb(cb), strengthRatio(strengthRatio), battleID(battleID) { @@ -73,7 +73,7 @@ public: const battle::Unit * activeStack, PlayerColor playerID, BattleID battleID, - int side, + BattleSide side, float strengthRatio) :scoreEvaluator(cb->getBattle(battleID), env, strengthRatio), cachedAttack(), playerID(playerID), side(side), env(env), cb(cb), hb(hb), damageCache(damageCache), strengthRatio(strengthRatio), battleID(battleID) { diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index 0200915c9..7755dec9c 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -500,7 +500,7 @@ BattleScore BattleExchangeEvaluator::calculateExchange( logAi->trace("Battle exchange at %d", ap.attack.shooting ? ap.dest.hex : ap.from.hex); #endif - if(cb->battleGetMySide() == BattlePerspective::LEFT_SIDE + if(cb->battleGetMySide() == BattleSide::LEFT_SIDE && cb->battleGetGateState() == EGateState::BLOCKED && ap.attack.defender->coversPos(BattleHex::GATE_BRIDGE)) { diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index 1f6711612..c6dc197da 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/AI/BattleAI/StackWithBonuses.cpp @@ -116,7 +116,7 @@ uint32_t StackWithBonuses::unitId() const return id; } -ui8 StackWithBonuses::unitSide() const +BattleSide StackWithBonuses::unitSide() const { return side; } @@ -467,7 +467,7 @@ int64_t HypotheticBattle::getActualDamage(const DamageRange & damage, int32_t at return (damage.min + damage.max) / 2; } -std::vector HypotheticBattle::getUsedSpells(ui8 side) const +std::vector HypotheticBattle::getUsedSpells(BattleSide side) const { // TODO return {}; diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index b3a705820..118dde639 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/AI/BattleAI/StackWithBonuses.h @@ -24,7 +24,7 @@ class HypotheticBattle; class RNGStub final : public vstd::RNG { public: - virtual int nextInt() override + int nextInt() override { return 0; } @@ -85,7 +85,7 @@ public: int32_t unitBaseAmount() const override; uint32_t unitId() const override; - ui8 unitSide() const override; + BattleSide unitSide() const override; PlayerColor unitOwner() const override; SlotID unitSlot() const override; @@ -111,7 +111,7 @@ private: const CCreature * type; ui32 baseAmount; uint32_t id; - ui8 side; + BattleSide side; PlayerColor player; SlotID slot; }; @@ -158,7 +158,7 @@ public: uint32_t nextUnitId() const override; int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const override; - std::vector getUsedSpells(ui8 side) const override; + std::vector getUsedSpells(BattleSide side) const override; int3 getLocation() const override; bool isCreatureBank() const override; diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index bee760d7d..1a105d5f8 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1148,7 +1148,7 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re } } -void AIGateway::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) +void AIGateway::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) { NET_EVENT_HANDLER; assert(!playerID.isValidPlayer() || status.getBattle() == UPCOMING_BATTLE); diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index fde31a730..a4a8a845a 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -156,7 +156,7 @@ public: void showWorldViewEx(const std::vector & objectPositions, bool showTerrain) override; std::optional makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override; - void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override; + void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override; void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override; void makeTurn(); diff --git a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp index 9666bb3e4..2d31c356d 100644 --- a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp @@ -31,16 +31,14 @@ void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo) } } - BuildingID prefixes[] = {BuildingID::DWELL_UP_FIRST, BuildingID::DWELL_FIRST}; - - for(int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++) + for(int level = 0; level < developmentInfo.town->town->creatures.size(); level++) { logAi->trace("Checking dwelling level %d", level); BuildingInfo nextToBuild = BuildingInfo(); - for(BuildingID prefix : prefixes) + for(int upgradeIndex : {1, 0}) { - BuildingID building = BuildingID(prefix + level); + BuildingID building = BuildingID(BuildingID::getDwellingFromLevel(level, upgradeIndex)); if(!vstd::contains(buildings, building)) continue; // no such building in town @@ -211,8 +209,8 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite( if(BuildingID::DWELL_FIRST <= toBuild && toBuild <= BuildingID::DWELL_UP_LAST) { - creatureLevel = (toBuild - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN; - creatureUpgrade = (toBuild - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN; + creatureLevel = BuildingID::getLevelFromDwelling(toBuild); + creatureUpgrade = BuildingID::getUpgradedFromDwelling(toBuild); } else if(toBuild == BuildingID::HORDE_1 || toBuild == BuildingID::HORDE_1_UPGR) { diff --git a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp index 37480f305..576dedfeb 100644 --- a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp +++ b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp @@ -212,7 +212,7 @@ void CaptureObjectsBehavior::decomposeObjects( vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit, specificObjects)); } - std::lock_guard lock(sync); // FIXME: consider using tbb::parallel_reduce instead to avoid mutex overhead + std::lock_guard lock(sync); // FIXME: consider using tbb::parallel_reduce instead to avoid mutex overhead vstd::concatenate(result, tasksLocal); }); } diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index ac51dd1e9..54f5b89b1 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -141,12 +141,10 @@ int32_t getResourcesGoldReward(const TResources & res) { int32_t result = 0; - for(EGameResID r = EGameResID(0); r < EGameResID::COUNT; r.advance(1)) + for(auto r : GameResID::ALL_RESOURCES()) { if(res[r] > 0) - { result += r == EGameResID::GOLD ? res[r] : res[r] * 100; - } } return result; @@ -350,7 +348,7 @@ uint64_t RewardEvaluator::getArmyReward( { for(auto artID : info.reward.artifacts) { - const CArtifact * art = dynamic_cast(VLC->artifacts()->getById(artID)); + const auto * art = dynamic_cast(VLC->artifacts()->getById(artID)); rewardValue += evaluateArtifactArmyValue(art); } @@ -358,7 +356,7 @@ uint64_t RewardEvaluator::getArmyReward( if(!info.reward.creatures.empty()) { - for(auto stackInfo : info.reward.creatures) + for(const auto & stackInfo : info.reward.creatures) { rewardValue += stackInfo.getType()->getAIValue() * stackInfo.getCount(); } diff --git a/AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp b/AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp index 539a93fda..de20fb439 100644 --- a/AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp +++ b/AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp @@ -321,7 +321,7 @@ void ObjectGraphCalculator::addObjectActor(const CGObjectInstance * obj) void ObjectGraphCalculator::addJunctionActor(const int3 & visitablePos, bool isVirtualBoat) { - std::lock_guard lock(syncLock); + std::lock_guard lock(syncLock); auto internalCb = temporaryActorHeroes.front()->cb; auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique(internalCb)).get(); diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 3c152de13..c0ccdf967 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -18,7 +18,7 @@ #include "../../lib/CRandomGenerator.h" CStupidAI::CStupidAI() - : side(-1) + : side(BattleSide::NONE) , wasWaitingForRealize(false) , wasUnlockingGs(false) { @@ -262,7 +262,7 @@ void CStupidAI::battleStacksEffectsSet(const BattleID & battleID, const SetStack print("battleStacksEffectsSet called"); } -void CStupidAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed) +void CStupidAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide Side, bool replayAllowed) { print("battleStart called"); side = Side; diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index 7c81a864c..302bd5590 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -17,7 +17,7 @@ class EnemyInfo; class CStupidAI : public CBattleGameInterface { - int side; + BattleSide side; std::shared_ptr cb; std::shared_ptr env; @@ -47,7 +47,7 @@ public: void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks //void battleTriggerEffect(const BattleTriggerEffect & bte) override; - void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right + void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack private: diff --git a/AI/VCAI/BuildingManager.cpp b/AI/VCAI/BuildingManager.cpp index b45306090..365b83b23 100644 --- a/AI/VCAI/BuildingManager.cpp +++ b/AI/VCAI/BuildingManager.cpp @@ -143,9 +143,9 @@ static const std::vector basicGoldSource = { BuildingID::TOWN_HALL, static const std::vector defence = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE }; static const std::vector capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL }; static const std::vector unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, -BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 }; +BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7, BuildingID::DWELL_LVL_8 }; static const std::vector unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, -BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP }; +BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP, BuildingID::DWELL_LVL_8_UP }; static const std::vector unitGrowth = { BuildingID::HORDE_1, BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR }; static const std::vector _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3, BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 }; diff --git a/AI/VCAI/Goals/GatherTroops.cpp b/AI/VCAI/Goals/GatherTroops.cpp index ed4e1a6c0..275bef9f5 100644 --- a/AI/VCAI/Goals/GatherTroops.cpp +++ b/AI/VCAI/Goals/GatherTroops.cpp @@ -109,7 +109,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() if(upgradeNumber < 0) continue; - BuildingID bid(BuildingID::DWELL_FIRST + creature->getLevel() - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN); + BuildingID bid(BuildingID::DWELL_FIRST + creature->getLevel() - 1 + upgradeNumber * t->town->creatures.size()); if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->getFullRecruitCost())) //this assumes only creatures with dwellings are assigned to faction { solutions.push_back(sptr(BuyArmy(t, creature->getAIValue() * this->value).setobjid(objid))); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index ef33d96c3..e00a119c9 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1566,7 +1566,7 @@ void VCAI::completeGoal(Goals::TSubgoal goal) } -void VCAI::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) +void VCAI::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) { NET_EVENT_HANDLER; assert(!playerID.isValidPlayer() || status.getBattle() == UPCOMING_BATTLE); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 8537e8f01..57f68de19 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -187,7 +187,7 @@ public: void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override; void showWorldViewEx(const std::vector & objectPositions, bool showTerrain) override; - void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override; + void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override; void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override; std::optional makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index d3dc88c9f..b55c5bdd6 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -624,7 +624,7 @@ void CPlayerInterface::battleStartBefore(const BattleID & battleID, const CCreat waitForAllDialogs(); } -void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) +void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) { EVENT_HANDLER_CALLED_BY_CLIENT; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index c5d682621..0ae02576c 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -160,7 +160,7 @@ protected: // Call-ins from server, should not be called directly, but only via void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect void battleStacksAttacked(const BattleID & battleID, const std::vector & bsa, bool ranged) override; void battleStartBefore(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right - void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right + void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right void battleUnitsChanged(const BattleID & battleID, const std::vector & units) override; void battleObstaclesChanged(const BattleID & battleID, const std::vector & obstacles) override; void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack diff --git a/client/Client.cpp b/client/Client.cpp index 174acdb39..8de7c4157 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -443,8 +443,8 @@ void CClient::battleStarted(const BattleInfo * info) { std::shared_ptr att; std::shared_ptr def; - auto & leftSide = info->sides[0]; - auto & rightSide = info->sides[1]; + const auto & leftSide = info->getSide(BattleSide::LEFT_SIDE); + const auto & rightSide = info->getSide(BattleSide::RIGHT_SIDE); for(auto & battleCb : battleCallbacks) { @@ -453,17 +453,17 @@ void CClient::battleStarted(const BattleInfo * info) } //If quick combat is not, do not prepare interfaces for battleint - auto callBattleStart = [&](PlayerColor color, ui8 side) + auto callBattleStart = [&](PlayerColor color, BattleSide side) { if(vstd::contains(battleints, color)) battleints[color]->battleStart(info->battleID, leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side, info->replayAllowed); }; - callBattleStart(leftSide.color, 0); - callBattleStart(rightSide.color, 1); - callBattleStart(PlayerColor::UNFLAGGABLE, 1); + callBattleStart(leftSide.color, BattleSide::LEFT_SIDE); + callBattleStart(rightSide.color, BattleSide::RIGHT_SIDE); + callBattleStart(PlayerColor::UNFLAGGABLE, BattleSide::RIGHT_SIDE); if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool()) - callBattleStart(PlayerColor::SPECTATOR, 1); + callBattleStart(PlayerColor::SPECTATOR, BattleSide::RIGHT_SIDE); if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human) att = std::dynamic_pointer_cast(playerint[leftSide.color]); @@ -480,9 +480,9 @@ void CClient::battleStarted(const BattleInfo * info) { auto side = interface->cb->getBattle(info->battleID)->playerToSide(interface->playerID); - if(interface->playerID == info->sides[info->tacticsSide].color) + if(interface->playerID == info->getSide(info->tacticsSide).color) { - auto action = BattleAction::makeEndOFTacticPhase(*side); + auto action = BattleAction::makeEndOFTacticPhase(side); interface->cb->battleMakeTacticAction(info->battleID, action); } } @@ -514,7 +514,7 @@ void CClient::battleStarted(const BattleInfo * info) if(info->tacticDistance) { - auto tacticianColor = info->sides[info->tacticsSide].color; + auto tacticianColor = info->getSide(info->tacticsSide).color; if (vstd::contains(battleints, tacticianColor)) battleints[tacticianColor]->yourTacticPhase(info->battleID, info->tacticDistance); @@ -523,9 +523,11 @@ void CClient::battleStarted(const BattleInfo * info) void CClient::battleFinished(const BattleID & battleID) { - for(auto & side : gs->getBattle(battleID)->sides) - if(battleCallbacks.count(side.color)) - battleCallbacks[side.color]->onBattleEnded(battleID); + for(auto side : { BattleSide::ATTACKER, BattleSide::DEFENDER }) + { + if(battleCallbacks.count(gs->getBattle(battleID)->getSide(side).color)) + battleCallbacks[gs->getBattle(battleID)->getSide(side).color]->onBattleEnded(battleID); + } if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool()) battleCallbacks[PlayerColor::SPECTATOR]->onBattleEnded(battleID); diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 84767e1b9..05f6fe038 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -108,8 +108,8 @@ void callBattleInterfaceIfPresentForBothSides(CClient & cl, const BattleID & bat return; } - callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->sides[0].color, ptr, std::forward(args)...); - callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->sides[1].color, ptr, std::forward(args)...); + callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::ATTACKER).color, ptr, std::forward(args)...); + callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::DEFENDER).color, ptr, std::forward(args)...); if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool() && LOCPLINT->battleInt) { callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, ptr, std::forward(args)...); @@ -769,12 +769,12 @@ void ApplyClientNetPackVisitor::visitMapObjectSelectDialog(MapObjectSelectDialog void ApplyFirstClientNetPackVisitor::visitBattleStart(BattleStart & pack) { // Cannot use the usual code because curB is not set yet - callOnlyThatBattleInterface(cl, pack.info->sides[0].color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->sides[0].armyObject, pack.info->sides[1].armyObject, - pack.info->tile, pack.info->sides[0].hero, pack.info->sides[1].hero); - callOnlyThatBattleInterface(cl, pack.info->sides[1].color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->sides[0].armyObject, pack.info->sides[1].armyObject, - pack.info->tile, pack.info->sides[0].hero, pack.info->sides[1].hero); - callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->sides[0].armyObject, pack.info->sides[1].armyObject, - pack.info->tile, pack.info->sides[0].hero, pack.info->sides[1].hero); + callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::ATTACKER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject, + pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero); + callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::DEFENDER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject, + pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero); + callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject, + pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero); } void ApplyClientNetPackVisitor::visitBattleStart(BattleStart & pack) @@ -801,9 +801,9 @@ void ApplyClientNetPackVisitor::visitBattleSetActiveStack(BattleSetActiveStack & PlayerColor playerToCall; //pack.player that will move activated stack if (activated->hasBonusOfType(BonusType::HYPNOTIZED)) { - playerToCall = (gs.getBattle(pack.battleID)->sides[0].color == activated->unitOwner() - ? gs.getBattle(pack.battleID)->sides[1].color - : gs.getBattle(pack.battleID)->sides[0].color); + playerToCall = gs.getBattle(pack.battleID)->getSide(BattleSide::ATTACKER).color == activated->unitOwner() + ? gs.getBattle(pack.battleID)->getSide(BattleSide::DEFENDER).color + : gs.getBattle(pack.battleID)->getSide(BattleSide::ATTACKER).color; } else { diff --git a/client/adventureMap/AdventureMapShortcuts.cpp b/client/adventureMap/AdventureMapShortcuts.cpp index 17e5ebcdd..fe7253cb0 100644 --- a/client/adventureMap/AdventureMapShortcuts.cpp +++ b/client/adventureMap/AdventureMapShortcuts.cpp @@ -530,7 +530,6 @@ bool AdventureMapShortcuts::optionCanVisitObject() auto * hero = LOCPLINT->localState->getCurrentHero(); auto objects = LOCPLINT->cb->getVisitableObjs(hero->visitablePos()); - //assert(vstd::contains(objects,hero)); return objects.size() > 1; // there is object other than our hero } diff --git a/client/adventureMap/CList.cpp b/client/adventureMap/CList.cpp index 9e3b4f011..f65a1ed7d 100644 --- a/client/adventureMap/CList.cpp +++ b/client/adventureMap/CList.cpp @@ -18,10 +18,12 @@ #include "../widgets/ObjectLists.h" #include "../widgets/RadialMenu.h" #include "../windows/InfoWindows.h" +#include "../windows/CCastleInterface.h" #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../PlayerLocalState.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../gui/WindowHandler.h" #include "../render/Canvas.h" #include "../render/Colors.h" @@ -32,6 +34,8 @@ #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" +#include "../../CCallback.h" + CList::CListItem::CListItem(CList * Parent) : CIntObject(LCLICK | SHOW_POPUP | HOVER), parent(Parent), @@ -229,7 +233,7 @@ CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero) update(); - addUsedEvents(GESTURE); + addUsedEvents(GESTURE | KEYBOARD); } void CHeroList::CHeroItem::update() @@ -300,6 +304,55 @@ void CHeroList::CHeroItem::gesture(bool on, const Point & initialPosition, const GH.windows().createAndPushWindow(pos.center(), menuElements, true); } +void CHeroList::CHeroItem::keyPressed(EShortcut key) +{ + if(!hero) + return; + + if(parent->selected != this->shared_from_this()) + return; + + auto & heroes = LOCPLINT->localState->getWanderingHeroes(); + + if(key == EShortcut::LIST_HERO_DISMISS) + { + LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], [=](){ LOCPLINT->cb->dismissHero(hero); }, nullptr); + return; + } + + if(heroes.size() < 2) + return; + + size_t heroPos = vstd::find_pos(heroes, hero); + const CGHeroInstance * heroUpper = (heroPos < 1) ? nullptr : heroes.at(heroPos - 1); + const CGHeroInstance * heroLower = (heroPos > heroes.size() - 2) ? nullptr : heroes.at(heroPos + 1); + + switch(key) + { + case EShortcut::LIST_HERO_UP: + if(heroUpper) + LOCPLINT->localState->swapWanderingHero(heroPos, heroPos - 1); + break; + + case EShortcut::LIST_HERO_DOWN: + if(heroLower) + LOCPLINT->localState->swapWanderingHero(heroPos, heroPos + 1); + break; + + case EShortcut::LIST_HERO_TOP: + if(heroUpper) + for (size_t i = heroPos; i > 0; i--) + LOCPLINT->localState->swapWanderingHero(i, i - 1); + break; + + case EShortcut::LIST_HERO_BOTTOM: + if(heroLower) + for (int i = heroPos; i < heroes.size() - 1; i++) + LOCPLINT->localState->swapWanderingHero(i, i + 1); + break; + } +} + std::shared_ptr CHeroList::createItem(size_t index) { if (LOCPLINT->localState->getWanderingHeroes().size() > index) @@ -369,7 +422,7 @@ CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town): pos = picture->pos; update(); - addUsedEvents(GESTURE); + addUsedEvents(GESTURE | KEYBOARD); } std::shared_ptr CTownList::CTownItem::genSelection() @@ -418,24 +471,77 @@ void CTownList::CTownItem::gesture(bool on, const Point & initialPosition, const int townUpperPos = (townIndex < 1) ? -1 : townIndex - 1; int townLowerPos = (townIndex > towns.size() - 2) ? -1 : townIndex + 1; + auto updateList = [](){ + for (auto ki : GH.windows().findWindows()) + ki->townChange(); //update list + }; + std::vector menuElements = { - { RadialMenuConfig::ITEM_ALT_NN, townUpperPos > -1, "altUpTop", "vcmi.radialWheel.moveTop", [townIndex]() + { RadialMenuConfig::ITEM_ALT_NN, townUpperPos > -1, "altUpTop", "vcmi.radialWheel.moveTop", [updateList, townIndex]() { for (int i = townIndex; i > 0; i--) LOCPLINT->localState->swapOwnedTowns(i, i - 1); + updateList(); } }, - { RadialMenuConfig::ITEM_ALT_NW, townUpperPos > -1, "altUp", "vcmi.radialWheel.moveUp", [townIndex, townUpperPos](){LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); } }, - { RadialMenuConfig::ITEM_ALT_SW, townLowerPos > -1, "altDown", "vcmi.radialWheel.moveDown", [townIndex, townLowerPos](){ LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); } }, - { RadialMenuConfig::ITEM_ALT_SS, townLowerPos > -1, "altDownBottom", "vcmi.radialWheel.moveBottom", [townIndex, towns]() + { RadialMenuConfig::ITEM_ALT_NW, townUpperPos > -1, "altUp", "vcmi.radialWheel.moveUp", [updateList, townIndex, townUpperPos](){LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); updateList(); } }, + { RadialMenuConfig::ITEM_ALT_SW, townLowerPos > -1, "altDown", "vcmi.radialWheel.moveDown", [updateList, townIndex, townLowerPos](){ LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); updateList(); } }, + { RadialMenuConfig::ITEM_ALT_SS, townLowerPos > -1, "altDownBottom", "vcmi.radialWheel.moveBottom", [updateList, townIndex, towns]() { for (int i = townIndex; i < towns.size() - 1; i++) LOCPLINT->localState->swapOwnedTowns(i, i + 1); + updateList(); } }, }; GH.windows().createAndPushWindow(pos.center(), menuElements, true); } +void CTownList::CTownItem::keyPressed(EShortcut key) +{ + if(parent->selected != this->shared_from_this()) + return; + + const std::vector towns = LOCPLINT->localState->getOwnedTowns(); + size_t townIndex = vstd::find_pos(towns, town); + + if(townIndex + 1 > towns.size() || !towns.at(townIndex)) + return; + + if(towns.size() < 2) + return; + + int townUpperPos = (townIndex < 1) ? -1 : townIndex - 1; + int townLowerPos = (townIndex > towns.size() - 2) ? -1 : townIndex + 1; + + switch(key) + { + case EShortcut::LIST_TOWN_UP: + if(townUpperPos > -1) + LOCPLINT->localState->swapOwnedTowns(townIndex, townUpperPos); + break; + + case EShortcut::LIST_TOWN_DOWN: + if(townLowerPos > -1) + LOCPLINT->localState->swapOwnedTowns(townIndex, townLowerPos); + break; + + case EShortcut::LIST_TOWN_TOP: + if(townUpperPos > -1) + for (int i = townIndex; i > 0; i--) + LOCPLINT->localState->swapOwnedTowns(i, i - 1); + break; + + case EShortcut::LIST_TOWN_BOTTOM: + if(townLowerPos > -1) + for (int i = townIndex; i < towns.size() - 1; i++) + LOCPLINT->localState->swapOwnedTowns(i, i + 1); + break; + } + + for (auto ki : GH.windows().findWindows()) + ki->townChange(); //update list +} + std::string CTownList::CTownItem::getHoverText() { return town->getObjectName(); diff --git a/client/adventureMap/CList.h b/client/adventureMap/CList.h index 5b03f2b1e..bccd4fecf 100644 --- a/client/adventureMap/CList.h +++ b/client/adventureMap/CList.h @@ -29,9 +29,10 @@ class CList : public Scrollable protected: class CListItem : public CIntObject, public std::enable_shared_from_this { - CList * parent; std::shared_ptr selection; public: + CList * parent; + CListItem(CList * parent); ~CListItem(); @@ -55,9 +56,6 @@ protected: private: const size_t size; - - //for selection\deselection - std::shared_ptr selected; void select(std::shared_ptr which); friend class CListItem; @@ -81,6 +79,9 @@ protected: void update(); public: + //for selection\deselection + std::shared_ptr selected; + /// functions that will be called when selection changes CFunctionList onSelect; @@ -128,6 +129,7 @@ class CHeroList : public CList void open() override; void showTooltip() override; void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; + void keyPressed(EShortcut key) override; std::string getHoverText() override; }; @@ -162,6 +164,7 @@ class CTownList : public CList void open() override; void showTooltip() override; void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; + void keyPressed(EShortcut key) override; std::string getHoverText() override; }; diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index b182cb400..8635f24d1 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -312,8 +312,8 @@ void BattleActionsController::castThisSpell(SpellID spellID) heroSpellToCast = std::make_shared(); heroSpellToCast->actionType = EActionType::HERO_SPELL; heroSpellToCast->spell = spellID; - heroSpellToCast->stackNumber = (owner.attackingHeroInstance->tempOwner == owner.curInt->playerID) ? -1 : -2; - heroSpellToCast->side = owner.defendingHeroInstance ? (owner.curInt->playerID == owner.defendingHeroInstance->tempOwner) : false; + heroSpellToCast->stackNumber = -1; + heroSpellToCast->side = owner.curInt->cb->getBattle(owner.getBattleID())->battleGetMySide(); //choosing possible targets const CGHeroInstance *castingHero = (owner.attackingHeroInstance->tempOwner == owner.curInt->playerID) ? owner.attackingHeroInstance : owner.defendingHeroInstance; diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index 15f14baf8..826c401a1 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -229,19 +229,19 @@ void BattleInterface::stacksAreAttacked(std::vector attackedI { stacksController->stacksAreAttacked(attackedInfos); - std::array killedBySide = {0, 0}; + BattleSideArray killedBySide; for(const StackAttackedInfo & attackedInfo : attackedInfos) { - ui8 side = attackedInfo.defender->unitSide(); + BattleSide side = attackedInfo.defender->unitSide(); killedBySide.at(side) += attackedInfo.amountKilled; } - for(ui8 side = 0; side < 2; side++) + for(BattleSide side : { BattleSide::ATTACKER, BattleSide::DEFENDER }) { - if(killedBySide.at(side) > killedBySide.at(1-side)) + if(killedBySide.at(side) > killedBySide.at(getBattle()->otherSide(side))) setHeroAnimation(side, EHeroAnimType::DEFEAT); - else if(killedBySide.at(side) < killedBySide.at(1-side)) + else if(killedBySide.at(side) < killedBySide.at(getBattle()->otherSide(side))) setHeroAnimation(side, EHeroAnimType::VICTORY); } } @@ -271,14 +271,14 @@ void BattleInterface::giveCommand(EActionType action, BattleHex tile, SpellID sp } auto side = getBattle()->playerToSide(curInt->playerID); - if(!side) + if(side == BattleSide::NONE) { logGlobal->error("Player %s is not in battle", curInt->playerID.toString()); return; } BattleAction ba; - ba.side = side.value(); + ba.side = side; ba.actionType = action; ba.aimToHex(tile); ba.spell = spell; @@ -409,7 +409,7 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) } else { - auto hero = sc->side ? defendingHero : attackingHero; + auto hero = sc->side == BattleSide::DEFENDER ? defendingHero : attackingHero; assert(hero); addToAnimationStage(EAnimationEvents::BEFORE_HIT, [=]() @@ -466,11 +466,11 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) { Point leftHero = Point(15, 30); Point rightHero = Point(755, 30); - bool side = sc->side; + BattleSide side = sc->side; addToAnimationStage(EAnimationEvents::AFTER_HIT, [=](){ - stacksController->addNewAnim(new EffectAnimation(*this, AnimationPath::builtin(side ? "SP07_A.DEF" : "SP07_B.DEF"), leftHero)); - stacksController->addNewAnim(new EffectAnimation(*this, AnimationPath::builtin(side ? "SP07_B.DEF" : "SP07_A.DEF"), rightHero)); + stacksController->addNewAnim(new EffectAnimation(*this, AnimationPath::builtin(side == BattleSide::DEFENDER ? "SP07_A.DEF" : "SP07_B.DEF"), leftHero)); + stacksController->addNewAnim(new EffectAnimation(*this, AnimationPath::builtin(side == BattleSide::DEFENDER ? "SP07_B.DEF" : "SP07_A.DEF"), rightHero)); }); } @@ -483,7 +483,7 @@ void BattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) fieldController->redrawBackgroundWithHexes(); } -void BattleInterface::setHeroAnimation(ui8 side, EHeroAnimType phase) +void BattleInterface::setHeroAnimation(BattleSide side, EHeroAnimType phase) { if(side == BattleSide::ATTACKER) { @@ -656,7 +656,7 @@ void BattleInterface::tacticPhaseEnd() tacticsMode = false; auto side = tacticianInterface->cb->getBattle(battleID)->playerToSide(tacticianInterface->playerID); - auto action = BattleAction::makeEndOFTacticPhase(*side); + auto action = BattleAction::makeEndOFTacticPhase(side); tacticianInterface->cb->battleMakeTacticAction(battleID, action); } diff --git a/client/battle/BattleInterface.h b/client/battle/BattleInterface.h index 1281d5ca4..a7c578ecf 100644 --- a/client/battle/BattleInterface.h +++ b/client/battle/BattleInterface.h @@ -170,7 +170,7 @@ public: void showInterface(Canvas & to); - void setHeroAnimation(ui8 side, EHeroAnimType phase); + void setHeroAnimation(BattleSide side, EHeroAnimType phase); void executeSpellCast(); //called when a hero casts a spell diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 7a2035fc5..1f9413f49 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -433,7 +433,7 @@ QuickSpellPanel::QuickSpellPanel(BattleInterface & owner) create(); } -std::vector> QuickSpellPanel::getSpells() +std::vector> QuickSpellPanel::getSpells() const { std::vector spellIds; std::vector spellIdsFromSetting; @@ -746,7 +746,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface labels.push_back(std::make_shared(232, 520, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.battleResultsWindow.applyResultsLabel"))); } - if(br.winner == 0) //attacker won + if(br.winner == BattleSide::ATTACKER) { labels.push_back(std::make_shared(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[410])); } @@ -754,8 +754,8 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface { labels.push_back(std::make_shared(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[411])); } - - if(br.winner == 1) + + if(br.winner == BattleSide::DEFENDER) { labels.push_back(std::make_shared(412, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[410])); } @@ -770,15 +770,15 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface std::string sideNames[2] = {"N/A", "N/A"}; - for(int i = 0; i < 2; i++) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { auto heroInfo = owner.cb->getBattle(br.battleID)->battleGetHeroInfo(i); const int xs[] = {21, 392}; if(heroInfo.portraitSource.isValid()) //attacking hero { - icons.push_back(std::make_shared(AnimationPath::builtin("PortraitsLarge"), heroInfo.getIconIndex(), 0, xs[i], 38)); - sideNames[i] = heroInfo.name; + icons.push_back(std::make_shared(AnimationPath::builtin("PortraitsLarge"), heroInfo.getIconIndex(), 0, xs[static_cast(i)], 38)); + sideNames[static_cast(i)] = heroInfo.name; } else { @@ -795,8 +795,8 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface if(best != stacks.end()) //should be always but to be safe... { - icons.push_back(std::make_shared(AnimationPath::builtin("TWCRPORT"), (*best)->unitType()->getIconIndex(), 0, xs[i], 38)); - sideNames[i] = (*best)->unitType()->getNamePluralTranslated(); + icons.push_back(std::make_shared(AnimationPath::builtin("TWCRPORT"), (*best)->unitType()->getIconIndex(), 0, xs[static_cast(i)], 38)); + sideNames[static_cast(i)] = (*best)->unitType()->getNamePluralTranslated(); } } } @@ -806,16 +806,16 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface labels.push_back(std::make_shared(381, 53, FONT_SMALL, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, sideNames[1])); //printing casualties - for(int step = 0; step < 2; ++step) + for(auto step : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { if(br.casualties[step].size()==0) { - labels.push_back(std::make_shared(235, 360 + 97 * step, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[523])); + labels.push_back(std::make_shared(235, 360 + 97 * static_cast(step), FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[523])); } else { int xPos = 235 - ((int)br.casualties[step].size()*32 + ((int)br.casualties[step].size() - 1)*10)/2; //increment by 42 with each picture - int yPos = 344 + step * 97; + int yPos = 344 + static_cast(step) * 97; for(auto & elem : br.casualties[step]) { auto creature = CGI->creatures()->getByIndex(elem.first); @@ -842,9 +842,9 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface BattleResultResources BattleResultWindow::getResources(const BattleResult & br) { //printing result description - bool weAreAttacker = !(owner.cb->getBattle(br.battleID)->battleGetMySide()); + bool weAreAttacker = owner.cb->getBattle(br.battleID)->battleGetMySide() == BattleSide::ATTACKER; bool weAreDefender = !weAreAttacker; - bool weWon = (br.winner == 0 && weAreAttacker) || (br.winner == 1 && !weAreAttacker); + bool weWon = (br.winner == BattleSide::ATTACKER && weAreAttacker) || (br.winner == BattleSide::DEFENDER && !weAreAttacker); bool isSiege = owner.cb->getBattle(br.battleID)->battleGetDefendedTown() != nullptr; BattleResultResources resources; @@ -884,7 +884,7 @@ BattleResultResources BattleResultWindow::getResources(const BattleResult & br) { resources.resultText.appendTextID("core.genrltxt.305"); resources.resultText.replaceTextID(ourHero->getNameTranslated()); - resources.resultText.replaceNumber(br.exp[weAreAttacker ? 0 : 1]); + resources.resultText.replaceNumber(br.exp[weAreAttacker ? BattleSide::ATTACKER : BattleSide::DEFENDER]); } } else // we lose diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 2af8c6bbd..819ed3504 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -169,7 +169,7 @@ public: void create(); - std::vector> getSpells(); + std::vector> getSpells() const; void show(Canvas & to) override; void inputModeChanged(InputMode modi) override; diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 031d40ee9..e291ee62b 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -102,7 +102,7 @@ void BattleObstacleController::obstaclePlaced(const std::vectorplayerToSide(owner.curInt->playerID); - if(!oi->visibleForSide(side.value(), owner.getBattle()->battleHasNativeStack(side.value()))) + if(!oi->visibleForSide(side, owner.getBattle()->battleHasNativeStack(side))) continue; auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::ALPHA); diff --git a/client/battle/BattleOverlayLogVisualizer.cpp b/client/battle/BattleOverlayLogVisualizer.cpp index 20b9df4e0..101da5fd1 100644 --- a/client/battle/BattleOverlayLogVisualizer.cpp +++ b/client/battle/BattleOverlayLogVisualizer.cpp @@ -27,7 +27,7 @@ BattleOverlayLogVisualizer::BattleOverlayLogVisualizer( { } -void BattleOverlayLogVisualizer::drawText(BattleHex hex, int lineNumber, std::string text) +void BattleOverlayLogVisualizer::drawText(BattleHex hex, int lineNumber, const std::string & text) { Point offset = owner.fieldController->hexPositionLocal(hex).topLeft() + Point(20, 20); int h = graphics->fonts[EFonts::FONT_TINY]->getLineHeight(); diff --git a/client/battle/BattleOverlayLogVisualizer.h b/client/battle/BattleOverlayLogVisualizer.h index 9fd2cf30e..e8dd1bf7d 100644 --- a/client/battle/BattleOverlayLogVisualizer.h +++ b/client/battle/BattleOverlayLogVisualizer.h @@ -24,5 +24,5 @@ private: public: BattleOverlayLogVisualizer(BattleRenderer::RendererRef & target, BattleInterface & owner); - void drawText(BattleHex hex, int lineNumber, std::string text) override; + void drawText(BattleHex hex, int lineNumber, const std::string & text) override; }; diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index bc98d75b2..b6cfd7fd8 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -212,7 +212,7 @@ class EmptyStatusBar : public IStatusBar virtual void setEnteredText(const std::string & text){}; }; -class ObjectConstruction +class ObjectConstruction : boost::noncopyable { public: ObjectConstruction(CIntObject *obj); diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 62c20acb9..8ba2edb56 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -294,5 +294,15 @@ enum class EShortcut SPELLBOOK_TAB_ADVENTURE, SPELLBOOK_TAB_COMBAT, + LIST_HERO_UP, + LIST_HERO_DOWN, + LIST_HERO_TOP, + LIST_HERO_BOTTOM, + LIST_HERO_DISMISS, + LIST_TOWN_UP, + LIST_TOWN_DOWN, + LIST_TOWN_TOP, + LIST_TOWN_BOTTOM, + AFTER_LAST }; diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index d8057bbeb..e9c1f448d 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -275,6 +275,15 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"heroCostumeLoad9", EShortcut::HERO_COSTUME_LOAD_9 }, {"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE }, {"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT }, + {"listHeroUp", EShortcut::LIST_HERO_UP }, + {"listHeroDown", EShortcut::LIST_HERO_DOWN }, + {"listHeroTop", EShortcut::LIST_HERO_TOP }, + {"listHeroBottom", EShortcut::LIST_HERO_BOTTOM }, + {"listHeroDismiss", EShortcut::LIST_HERO_DISMISS }, + {"listTownUp", EShortcut::LIST_TOWN_UP }, + {"listTownDown", EShortcut::LIST_TOWN_DOWN }, + {"listTownTop", EShortcut::LIST_TOWN_TOP }, + {"listTownBottom", EShortcut::LIST_TOWN_BOTTOM }, {"mainMenuHotseat", EShortcut::MAIN_MENU_HOTSEAT }, {"mainMenuHostGame", EShortcut::MAIN_MENU_HOST_GAME }, {"mainMenuJoinGame", EShortcut::MAIN_MENU_JOIN_GAME }, diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index 049d9d53d..f3927e369 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -494,7 +494,7 @@ void OptionsTab::SelectionWindow::reopen() if(type == SelType::HERO && SEL->getStartInfo()->playerInfos.find(color)->second.castle == FactionID::RANDOM) close(); else{ - auto window = std::shared_ptr(new SelectionWindow(color, type, slider ? slider->getValue() : 0)); + auto window = std::make_shared(color, type, slider ? slider->getValue() : 0); close(); if(CSH->isMyColor(color) || CSH->isHost()) GH.windows().pushWindow(window); diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index daf5685fc..b4b431ad4 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -475,7 +475,7 @@ void CMultiMode::joinTCP() GH.windows().createAndPushWindow(getPlayersNames(), savedScreenType, false, ELoadMode::MULTI); } -const std::vector CMultiMode::getPlayersNames() +std::vector CMultiMode::getPlayersNames() { std::vector playerNames; diff --git a/client/mainmenu/CMainMenu.h b/client/mainmenu/CMainMenu.h index f850eaf77..811dec13e 100644 --- a/client/mainmenu/CMainMenu.h +++ b/client/mainmenu/CMainMenu.h @@ -97,7 +97,7 @@ public: void joinTCP(); /// Get all configured player names. The first name would always be present and initialized to its default value. - const std::vector getPlayersNames(); + std::vector getPlayersNames(); void onNameChange(std::string newText); }; diff --git a/client/mapView/MapOverlayLogVisualizer.cpp b/client/mapView/MapOverlayLogVisualizer.cpp index 856dab0a9..a73a72a8a 100644 --- a/client/mapView/MapOverlayLogVisualizer.cpp +++ b/client/mapView/MapOverlayLogVisualizer.cpp @@ -54,8 +54,8 @@ void MapOverlayLogVisualizer::drawLine(int3 start, int3 end) void MapOverlayLogVisualizer::drawText( int3 tile, int lineNumber, - std::string text, - std::optional background) + const std::string & text, + const std::optional & background) { const Point offset = Point(6, 6); diff --git a/client/mapView/MapOverlayLogVisualizer.h b/client/mapView/MapOverlayLogVisualizer.h index a4fa99016..c1d6bf254 100644 --- a/client/mapView/MapOverlayLogVisualizer.h +++ b/client/mapView/MapOverlayLogVisualizer.h @@ -29,5 +29,5 @@ private: public: MapOverlayLogVisualizer(Canvas & target, std::shared_ptr model); void drawLine(int3 start, int3 end) override; - void drawText(int3 tile, int lineNumber, std::string text, std::optional color) override; + void drawText(int3 tile, int lineNumber, const std::string & text, const std::optional & color) override; }; diff --git a/client/mapView/MapRendererContext.cpp b/client/mapView/MapRendererContext.cpp index d375cbd2c..239541ae5 100644 --- a/client/mapView/MapRendererContext.cpp +++ b/client/mapView/MapRendererContext.cpp @@ -291,7 +291,7 @@ ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates if (!tile.visitable) return {}; - auto * object = tile.visitableObjects.back(); + const auto * object = tile.visitableObjects.back(); if (object->getOwner() == LOCPLINT->playerID) return { 0, 192, 0}; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index cd75c6620..a52675a84 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -188,17 +188,6 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i init(); } -//CAnimImage::CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags): -// anim(Anim), -// frame(Frame), -// group(Group), -// flags(Flags) -//{ -// pos.x += x; -// pos.y += y; -// init(); -//} - CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags): anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY)), frame(Frame), diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 5e66276d1..463de77d3 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -114,7 +114,6 @@ public: bool visible; CAnimImage(const AnimationPath & name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); -// CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group=0, ui8 Flags=0); ~CAnimImage(); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 1987c7225..0daa2bd96 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -163,7 +163,7 @@ void CBuildingRect::showPopupWindow(const Point & cursorPosition) } else { - int level = ( bid - BuildingID::DWELL_FIRST ) % GameConstants::CREATURES_PER_TOWN; + int level = BuildingID::getLevelFromDwelling(bid); GH.windows().createAndPushWindow(parent->pos.x+parent->pos.w / 2, parent->pos.y+parent->pos.h /2, town, level); } } @@ -237,7 +237,7 @@ std::string CBuildingRect::getSubtitle()//hover text for building return town->town->buildings.at(getBuilding()->bid)->getNameTranslated(); else//dwellings - recruit %creature% { - auto & availableCreatures = town->creatures[(bid-30)%GameConstants::CREATURES_PER_TOWN].second; + auto & availableCreatures = town->creatures[(bid-30)%town->town->creatures.size()].second; if(availableCreatures.size()) { int creaID = availableCreatures.back();//taking last of available creatures @@ -688,7 +688,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil if (building >= BuildingID::DWELL_FIRST) { - enterDwelling((building-BuildingID::DWELL_FIRST)%GameConstants::CREATURES_PER_TOWN); + enterDwelling((BuildingID::getLevelFromDwelling(building))); } else { @@ -800,10 +800,10 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil break; case BuildingSubID::PORTAL_OF_SUMMONING: - if (town->creatures[GameConstants::CREATURES_PER_TOWN].second.empty())//No creatures + if (town->creatures[town->town->creatures.size()].second.empty())//No creatures LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]); else - enterDwelling(GameConstants::CREATURES_PER_TOWN); + enterDwelling(town->town->creatures.size()); break; case BuildingSubID::BALLISTA_YARD: @@ -921,8 +921,8 @@ void CCastleBuildings::enterDwelling(int level) void CCastleBuildings::enterToTheQuickRecruitmentWindow() { const auto beginIt = town->creatures.cbegin(); - const auto afterLastIt = town->creatures.size() > GameConstants::CREATURES_PER_TOWN - ? std::next(beginIt, GameConstants::CREATURES_PER_TOWN) + const auto afterLastIt = town->creatures.size() > town->town->creatures.size() + ? std::next(beginIt, town->town->creatures.size()) : town->creatures.cend(); const auto hasSomeoneToRecruit = std::any_of(beginIt, afterLastIt, [](const auto & creatureInfo) { return creatureInfo.first > 0; }); @@ -1759,7 +1759,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town): { OBJECT_CONSTRUCTION; ui32 fortSize = static_cast(town->creatures.size()); - if(fortSize > GameConstants::CREATURES_PER_TOWN && town->creatures.back().second.empty()) + if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty()) fortSize--; const CBuilding * fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6)); @@ -1777,25 +1777,25 @@ CFortScreen::CFortScreen(const CGTownInstance * town): if(fortSize == GameConstants::CREATURES_PER_TOWN) { - positions.push_back(Point(206,421)); + positions.push_back(Point(10, 421)); + positions.push_back(Point(404,421)); } else { - positions.push_back(Point(10, 421)); - positions.push_back(Point(404,421)); + positions.push_back(Point(206,421)); } for(ui32 i=0; itown->creatures.size()) { - BuildingID dwelling = BuildingID::DWELL_UP_FIRST+i; + BuildingID dwelling = BuildingID::getDwellingFromLevel(i, 1); if(vstd::contains(town->builtBuildings, dwelling)) - buildingID = BuildingID(BuildingID::DWELL_UP_FIRST+i); + buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 1)); else - buildingID = BuildingID(BuildingID::DWELL_FIRST+i); + buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 0)); } else { @@ -1817,13 +1817,13 @@ CFortScreen::CFortScreen(const CGTownInstance * town): ImagePath CFortScreen::getBgName(const CGTownInstance * town) { ui32 fortSize = static_cast(town->creatures.size()); - if(fortSize > GameConstants::CREATURES_PER_TOWN && town->creatures.back().second.empty()) + if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty()) fortSize--; if(fortSize == GameConstants::CREATURES_PER_TOWN) - return ImagePath::builtin("TPCASTL7"); - else return ImagePath::builtin("TPCASTL8"); + else + return ImagePath::builtin("TPCASTL7"); } void CFortScreen::creaturesChangedEventHandler() @@ -1897,9 +1897,9 @@ const CCreature * CFortScreen::RecruitArea::getMyCreature() const CBuilding * CFortScreen::RecruitArea::getMyBuilding() { - BuildingID myID = BuildingID(BuildingID::DWELL_FIRST + level); + BuildingID myID = BuildingID(BuildingID::getDwellingFromLevel(level, 0)); - if (level == GameConstants::CREATURES_PER_TOWN) + if (level == town->town->creatures.size()) return town->town->getSpecialBuilding(BuildingSubID::PORTAL_OF_SUMMONING); if (!town->town->buildings.count(myID)) @@ -1910,7 +1910,7 @@ const CBuilding * CFortScreen::RecruitArea::getMyBuilding() { if (town->hasBuilt(myID)) build = town->town->buildings.at(myID); - myID.advance(GameConstants::CREATURES_PER_TOWN); + myID.advance(town->town->creatures.size()); } return build; } diff --git a/client/windows/CMapOverview.cpp b/client/windows/CMapOverview.cpp index 4c122a78d..34bd07e7d 100644 --- a/client/windows/CMapOverview.cpp +++ b/client/windows/CMapOverview.cpp @@ -41,7 +41,7 @@ #include "../../lib/texts/CGeneralTextHandler.h" #include "../../lib/texts/TextOperations.h" -CMapOverview::CMapOverview(std::string mapName, std::string fileName, std::string date, std::string author, std::string version, ResourcePath resource, ESelectionScreen tabType) +CMapOverview::CMapOverview(const std::string & mapName, const std::string & fileName, const std::string & date, const std::string & author, const std::string & version, const ResourcePath & resource, ESelectionScreen tabType) : CWindowObject(BORDERED | RCLICK_POPUP), resource(resource), mapName(mapName), fileName(fileName), date(date), author(author), version(version), tabType(tabType) { diff --git a/client/windows/CMapOverview.h b/client/windows/CMapOverview.h index fbb70a6df..f1b7caf25 100644 --- a/client/windows/CMapOverview.h +++ b/client/windows/CMapOverview.h @@ -57,5 +57,5 @@ public: const std::string version; const ESelectionScreen tabType; - CMapOverview(std::string mapName, std::string fileName, std::string date, std::string author, std::string version, ResourcePath resource, ESelectionScreen tabType); + CMapOverview(const std::string & mapName, const std::string & fileName, const std::string & date, const std::string & author, const std::string & version, const ResourcePath & resource, ESelectionScreen tabType); }; diff --git a/client/windows/CPuzzleWindow.cpp b/client/windows/CPuzzleWindow.cpp index 3a39c52f1..35beae3b0 100644 --- a/client/windows/CPuzzleWindow.cpp +++ b/client/windows/CPuzzleWindow.cpp @@ -54,7 +54,7 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio) { const SPuzzleInfo & info = elem; - auto piece = std::make_shared(info.filename, info.x, info.y); + auto piece = std::make_shared(info.filename, info.position.x, info.position.y); //piece that will slowly disappear if(info.whenUncovered <= GameConstants::PUZZLE_MAP_PIECES * discoveredRatio) diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index e1371c8e2..eeb3b3ba8 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -98,7 +98,7 @@ public: } }; -CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells, std::function onSpellSelect): +CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells, const std::function & onSpellSelect): CWindowObject(PLAYER_COLORED | (settings["gameTweaks"]["enableLargeSpellbook"].Bool() ? BORDERED : 0)), battleSpellsOnly(openOnBattleSpells), selectedTab(4), diff --git a/client/windows/CSpellWindow.h b/client/windows/CSpellWindow.h index 3119860ca..8d62b616d 100644 --- a/client/windows/CSpellWindow.h +++ b/client/windows/CSpellWindow.h @@ -119,7 +119,7 @@ class CSpellWindow : public CWindowObject std::function onSpellSelect; //external processing of selected spell public: - CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true, std::function onSpellSelect = nullptr); + CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true, const std::function & onSpellSelect = nullptr); ~CSpellWindow(); void fexitb(); diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index b45a7f94b..ea512efc8 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -51,7 +51,7 @@ void QuickRecruitmentWindow::setCreaturePurchaseCards() { int availableAmount = getAvailableCreatures(); Point position = Point((pos.w - 100*availableAmount - 8*(availableAmount-1))/2,64); - for (int i = 0; i < GameConstants::CREATURES_PER_TOWN; i++) + for (int i = 0; i < town->town->creatures.size(); i++) { if(!town->town->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first) { @@ -106,7 +106,16 @@ void QuickRecruitmentWindow::purchaseUnits() { if(selected->slider->getValue()) { - auto onRecruit = [=](CreatureID id, int count){ LOCPLINT->cb->recruitCreatures(town, town->getUpperArmy(), id, count, selected->creatureOnTheCard->getLevel()-1); }; + int level = 0; + int i = 0; + for(auto c : town->town->creatures) + { + for(auto c2 : c) + if(c2 == selected->creatureOnTheCard->getId()) + level = i; + i++; + } + auto onRecruit = [=](CreatureID id, int count){ LOCPLINT->cb->recruitCreatures(town, town->getUpperArmy(), id, count, level); }; CreatureID crid = selected->creatureOnTheCard->getId(); SlotID dstslot = town -> getSlotFor(crid); if(!dstslot.validSlot()) @@ -120,7 +129,7 @@ void QuickRecruitmentWindow::purchaseUnits() int QuickRecruitmentWindow::getAvailableCreatures() { int creaturesAmount = 0; - for (int i=0; i< GameConstants::CREATURES_PER_TOWN; i++) + for (int i=0; i< town->town->creatures.size(); i++) if(!town->town->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first) creaturesAmount++; return creaturesAmount; diff --git a/config/campaign_regions.json b/config/campaign_regions.json index 1ae7b89c1..bcba4875b 100644 --- a/config/campaign_regions.json +++ b/config/campaign_regions.json @@ -2,7 +2,7 @@ "campaign_regions": [ { "prefix": "G1", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 57, "y": 314 }, { "infix": "B", "x": 137, "y": 309 }, @@ -12,7 +12,7 @@ { "prefix": "G2", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 56, "y": 90 }, { "infix": "B", "x": 316, "y": 49 }, @@ -23,7 +23,7 @@ { "prefix": "G3", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 289, "y": 376 }, { "infix": "B", "x": 60, "y": 147 }, @@ -33,7 +33,7 @@ { "prefix": "E1", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 270, "y": 332 }, { "infix": "B", "x": 138, "y": 113 }, @@ -47,7 +47,7 @@ { "prefix": "E2", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 131, "y": 202 }, { "infix": "B", "x": 60, "y": 145 }, @@ -58,7 +58,7 @@ { "prefix": "N1", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 42, "y": 94 }, { "infix": "B", "x": 309, "y": 290 }, @@ -68,7 +68,7 @@ { "prefix": "S1", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 263, "y": 199 }, { "infix": "B", "x": 182, "y": 210 }, @@ -78,7 +78,7 @@ { "prefix": "BR", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 18, "y": 233 }, { "infix": "B", "x": 125, "y": 381 }, @@ -89,7 +89,7 @@ { "prefix": "IS", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 294, "y": 399 }, { "infix": "B", "x": 183, "y": 293 }, @@ -100,7 +100,7 @@ { "prefix": "KR", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 148, "y": 323 }, { "infix": "B", "x": 192, "y": 235 }, @@ -111,7 +111,7 @@ { "prefix": "NI", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 118, "y": 111 }, { "infix": "B", "x": 223, "y": 145 }, @@ -122,7 +122,7 @@ { "prefix": "TA", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 228, "y": 233 }, { "infix": "B", "x": 147, "y": 194 }, @@ -132,7 +132,7 @@ { "prefix": "AR", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 135, "y": 238 }, { "infix": "B", "x": 135, "y": 121 }, @@ -147,7 +147,7 @@ { "prefix": "HS", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 141, "y": 326 }, { "infix": "B", "x": 238, "y": 275 }, @@ -158,7 +158,7 @@ { "prefix": "BB", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 167, "y": 342 }, { "infix": "B", "x": 217, "y": 263 }, @@ -170,7 +170,7 @@ { "prefix": "NB", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 6, "y": 292 }, { "infix": "B", "x": 161, "y": 334 }, @@ -181,7 +181,7 @@ { "prefix": "EL", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 11, "y": 73 }, { "infix": "B", "x": 0, "y": 241 }, @@ -192,7 +192,7 @@ { "prefix": "RN", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 84, "y": 319 }, { "infix": "B", "x": 194, "y": 275 }, @@ -203,7 +203,7 @@ { "prefix": "UA", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 157, "y": 409 }, { "infix": "B", "x": 62, "y": 346 }, @@ -222,7 +222,7 @@ { "prefix": "SP", - "color_suffix_length": 2, + "colorSuffixLength": 2, "desc": [ { "infix": "A", "x": 7, "y": 295 }, { "infix": "B", "x": 44, "y": 141 }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 2bcc70f2b..0fd6ad8e7 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -243,6 +243,15 @@ "townOpenThievesGuild": "G", "townOpenVisitingHero": "Ctrl+H", "townSwapArmies": "Space", + "listHeroUp": "Ctrl+PageUp", + "listHeroDown": "Ctrl+PageDown", + "listHeroTop": "Ctrl+Home", + "listHeroBottom": "Ctrl+End", + "listHeroDismiss": "Delete", + "listTownUp": "Ctrl+PageUp", + "listTownDown": "Ctrl+PageDown", + "listTownTop": "Ctrl+Home", + "listTownBottom": "Ctrl+End", // Controller-specific "mouseCursorX": [], diff --git a/docs/modders/Campaign_Format.md b/docs/modders/Campaign_Format.md index 6779a1e7c..a6a085f1d 100644 --- a/docs/modders/Campaign_Format.md +++ b/docs/modders/Campaign_Format.md @@ -182,8 +182,10 @@ Predefined campaign regions are located in file `campaign_regions.json` ```js { + "background": "ownRegionBackground.png", + "suffix": ["Enabled", "Selected", "Conquered"], "prefix": "G3", - "color_suffix_length": 1, + "colorSuffixLength": 1, "desc": [ { "infix": "A", "x": 289, "y": 376 }, { "infix": "B", "x": 60, "y": 147 }, @@ -192,9 +194,11 @@ Predefined campaign regions are located in file `campaign_regions.json` }, ``` -- `"prefix"` used to identify all images related to campaign. In this example, background picture will be `G3_BG` -- `"inflix"` ised to identify all images related to region. In this example, it will be pictures starting from `G3A_..., G3B_..., G3C_..."` -- `"color_suffix_length"` identifies suffix length for region colourful frames. 1 is used for `R, B, N, G, O, V, T, P`, value 2 is used for `Re, Bl, Br, Gr, Or, Vi, Te, Pi` +- `"background"` optional - use own image name for background instead of adding "_BG" to the prefix as name +- `"prefix"` used to identify all images related to campaign. In this example (if background parameter wouldn't exists), background picture will be `G3_BG` +- `"suffix"` optional - use other suffixes than the default `En`, `Se` and `Co` for the three different images +- `"infix"` used to identify all images related to region. In this example, it will be pictures whose files names begin with `G3A_..., G3B_..., G3C_..."` +- `"colorSuffixLength"` identifies suffix length for region colourful frames. 0 is no color suffix (no colorisation), 1 is used for `R, B, N, G, O, V, T, P`, value 2 is used for `Re, Bl, Br, Gr, Or, Vi, Te, Pi` ## Packing campaign diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 556ce44db..933650228 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -760,7 +760,7 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanc return nullptr; } -const ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance * artInst) const +ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance * artInst) const { if(artInst) { diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index f1ca9a738..0c9ac6dc4 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -207,7 +207,7 @@ public: /// Looks for equipped artifact with given ID and returns its slot ID or -1 if none /// (if more than one such artifact lower ID is returned) ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const; - const ArtifactPosition getArtPos(const CArtifactInstance * art) const; + ArtifactPosition getArtPos(const CArtifactInstance * art) const; std::vector getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const; std::vector getBackpackArtPositions(const ArtifactID & aid) const; const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const; diff --git a/lib/CConsoleHandler.cpp b/lib/CConsoleHandler.cpp index 8f1b0096e..c3dbd6fce 100644 --- a/lib/CConsoleHandler.cpp +++ b/lib/CConsoleHandler.cpp @@ -168,6 +168,7 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception) #endif +#ifdef NDEBUG [[noreturn]] static void onTerminate() { logGlobal->error("Disaster happened."); @@ -205,6 +206,7 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception) #endif std::abort(); } +#endif void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color) { @@ -296,14 +298,14 @@ CConsoleHandler::CConsoleHandler(): GetConsoleScreenBufferInfo(handleErr, &csbi); defErrColor = csbi.wAttributes; -#ifndef _DEBUG +#ifdef NDEBUG SetUnhandledExceptionFilter(onUnhandledException); #endif #else defColor = "\x1b[0m"; #endif -#ifndef _DEBUG +#ifdef NDEBUG std::set_terminate(onTerminate); #endif } diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index c23e5d613..1bd9f0910 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -765,7 +765,7 @@ void CCreatureHandler::loadCrExpBon(CBonusSystemNode & globalEffects) auto addBonusForTier = [&](int tier, std::shared_ptr b) { assert(vstd::iswithin(tier, 1, 7)); //bonuses from level 7 are given to high-level creatures too - auto max = tier == GameConstants::CREATURES_PER_TOWN ? std::numeric_limits::max() : tier + 1; + auto max = tier == 7 ? std::numeric_limits::max() : tier + 1; auto limiter = std::make_shared(tier, max); b->addLimiter(limiter); globalEffects.addNewBonus(b); diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index a705ea415..a62f1c83d 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -168,7 +168,7 @@ void CAdventureAI::battleCatapultAttacked(const BattleID & battleID, const Catap } void CAdventureAI::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, - const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) + const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) { assert(!battleAI); assert(cbc); diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index b0af48a49..3e65b79af 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -146,7 +146,7 @@ public: void battleNewRound(const BattleID & battleID) override; void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; - void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; + void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) override; void battleStacksAttacked(const BattleID & battleID, const std::vector & bsa, bool ranged) override; void actionStarted(const BattleID & battleID, const BattleAction &action) override; void battleNewRoundFirst(const BattleID & battleID) override; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e598a4389..1eaab7cd4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -403,6 +403,7 @@ set(lib_MAIN_HEADERS battle/BattleAttackInfo.h battle/BattleHex.h battle/BattleInfo.h + battle/BattleSide.h battle/BattleStateInfoForRetreat.h battle/BattleProxy.h battle/CBattleInfoCallback.h diff --git a/lib/CStack.cpp b/lib/CStack.cpp index fde17490c..0c3c6646a 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -24,7 +24,7 @@ VCMI_LIB_NAMESPACE_BEGIN ///CStack -CStack::CStack(const CStackInstance * Base, const PlayerColor & O, int I, ui8 Side, const SlotID & S): +CStack::CStack(const CStackInstance * Base, const PlayerColor & O, int I, BattleSide Side, const SlotID & S): CBonusSystemNode(STACK_BATTLE), base(Base), ID(I), @@ -45,7 +45,7 @@ CStack::CStack(): { } -CStack::CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I, ui8 Side, const SlotID & S): +CStack::CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I, BattleSide Side, const SlotID & S): CBonusSystemNode(STACK_BATTLE), ID(I), type(stack->type), @@ -367,7 +367,7 @@ uint32_t CStack::unitId() const return ID; } -ui8 CStack::unitSide() const +BattleSide CStack::unitSide() const { return side; } diff --git a/lib/CStack.h b/lib/CStack.h index 789c58969..456b44bc2 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -32,7 +32,7 @@ private: ui32 baseAmount = -1; PlayerColor owner; //owner - player color (255 for neutrals) - ui8 side = 1; + BattleSide side = BattleSide::NONE; SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures) @@ -41,8 +41,8 @@ public: BattleHex initialPosition; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower - CStack(const CStackInstance * base, const PlayerColor & O, int I, ui8 Side, const SlotID & S); - CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I, ui8 Side, const SlotID & S = SlotID(255)); + CStack(const CStackInstance * base, const PlayerColor & O, int I, BattleSide Side, const SlotID & S); + CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I, BattleSide Side, const SlotID & S = SlotID(255)); CStack(); ~CStack(); @@ -74,7 +74,7 @@ public: int32_t unitBaseAmount() const override; uint32_t unitId() const override; - ui8 unitSide() const override; + BattleSide unitSide() const override; PlayerColor unitOwner() const override; SlotID unitSlot() const override; diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 0e1c932f9..5f6c5540a 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -68,7 +68,7 @@ public: virtual void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse){};//called when a specific effect is set to stacks virtual void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte){}; //called for various one-shot effects virtual void battleStartBefore(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) {}; //called just before battle start - virtual void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed){}; //called by engine when battle starts; side=0 - left, side=1 - right + virtual void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed){}; //called by engine when battle starts; side=0 - left, side=1 - right virtual void battleUnitsChanged(const BattleID & battleID, const std::vector & units){}; virtual void battleObstaclesChanged(const BattleID & battleID, const std::vector & obstacles){}; virtual void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca){}; //called when catapult makes an attack diff --git a/lib/battle/AccessibilityInfo.cpp b/lib/battle/AccessibilityInfo.cpp index a7a29dae5..44e093f99 100644 --- a/lib/battle/AccessibilityInfo.cpp +++ b/lib/battle/AccessibilityInfo.cpp @@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN -bool AccessibilityInfo::tileAccessibleWithGate(BattleHex tile, ui8 side) const +bool AccessibilityInfo::tileAccessibleWithGate(BattleHex tile, BattleSide side) const { //at(otherHex) != EAccessibility::ACCESSIBLE && (at(otherHex) != EAccessibility::GATE || side != BattleSide::DEFENDER) if(at(tile) != EAccessibility::ACCESSIBLE) @@ -29,7 +29,7 @@ bool AccessibilityInfo::accessible(BattleHex tile, const battle::Unit * stack) c return accessible(tile, stack->doubleWide(), stack->unitSide()); } -bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, ui8 side) const +bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, BattleSide side) const { // All hexes that stack would cover if standing on tile have to be accessible. //do not use getHexes for speed reasons diff --git a/lib/battle/AccessibilityInfo.h b/lib/battle/AccessibilityInfo.h index dc3648412..f9604a490 100644 --- a/lib/battle/AccessibilityInfo.h +++ b/lib/battle/AccessibilityInfo.h @@ -37,9 +37,9 @@ struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray { public: bool accessible(BattleHex tile, const battle::Unit * stack) const; //checks for both tiles if stack is double wide - bool accessible(BattleHex tile, bool doubleWide, ui8 side) const; //checks for both tiles if stack is double wide + bool accessible(BattleHex tile, bool doubleWide, BattleSide side) const; //checks for both tiles if stack is double wide private: - bool tileAccessibleWithGate(BattleHex tile, ui8 side) const; + bool tileAccessibleWithGate(BattleHex tile, BattleSide side) const; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleAction.cpp b/lib/battle/BattleAction.cpp index e021cc1ba..8fe94b520 100644 --- a/lib/battle/BattleAction.cpp +++ b/lib/battle/BattleAction.cpp @@ -18,7 +18,7 @@ VCMI_LIB_NAMESPACE_BEGIN static const int32_t INVALID_UNIT_ID = -1000; BattleAction::BattleAction(): - side(-1), + side(BattleSide::NONE), stackNumber(-1), actionType(EActionType::NO_ACTION) { @@ -96,7 +96,7 @@ BattleAction BattleAction::makeMove(const battle::Unit * stack, BattleHex dest) return ba; } -BattleAction BattleAction::makeEndOFTacticPhase(ui8 side) +BattleAction BattleAction::makeEndOFTacticPhase(BattleSide side) { BattleAction ba; ba.side = side; @@ -104,7 +104,7 @@ BattleAction BattleAction::makeEndOFTacticPhase(ui8 side) return ba; } -BattleAction BattleAction::makeSurrender(ui8 side) +BattleAction BattleAction::makeSurrender(BattleSide side) { BattleAction ba; ba.side = side; @@ -112,7 +112,7 @@ BattleAction BattleAction::makeSurrender(ui8 side) return ba; } -BattleAction BattleAction::makeRetreat(ui8 side) +BattleAction BattleAction::makeRetreat(BattleSide side) { BattleAction ba; ba.side = side; diff --git a/lib/battle/BattleAction.h b/lib/battle/BattleAction.h index 40253b2a5..0c5aa533d 100644 --- a/lib/battle/BattleAction.h +++ b/lib/battle/BattleAction.h @@ -24,7 +24,7 @@ namespace battle class DLL_LINKAGE BattleAction { public: - ui8 side; //who made this action + BattleSide side; //who made this action ui32 stackNumber; //stack ID, -1 left hero, -2 right hero, EActionType actionType; //use ActionType enum for values @@ -39,9 +39,9 @@ public: static BattleAction makeShotAttack(const battle::Unit * shooter, const battle::Unit * target); static BattleAction makeCreatureSpellcast(const battle::Unit * stack, const battle::Target & target, const SpellID & spellID); static BattleAction makeMove(const battle::Unit * stack, BattleHex dest); - static BattleAction makeEndOFTacticPhase(ui8 side); - static BattleAction makeRetreat(ui8 side); - static BattleAction makeSurrender(ui8 side); + static BattleAction makeEndOFTacticPhase(BattleSide side); + static BattleAction makeRetreat(BattleSide side); + static BattleAction makeSurrender(BattleSide side); bool isTacticsAction() const; bool isUnitAction() const; diff --git a/lib/battle/BattleHex.cpp b/lib/battle/BattleHex.cpp index f6eaf319f..581cf538f 100644 --- a/lib/battle/BattleHex.cpp +++ b/lib/battle/BattleHex.cpp @@ -184,7 +184,7 @@ void BattleHex::checkAndPush(BattleHex tile, std::vector & ret) ret.push_back(tile); } -BattleHex BattleHex::getClosestTile(ui8 side, BattleHex initialPos, std::set & possibilities) +BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, std::set & possibilities) { std::vector sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :( BattleHex initialHex = BattleHex(initialPos); diff --git a/lib/battle/BattleHex.h b/lib/battle/BattleHex.h index 3b06b4828..0f1dc37e4 100644 --- a/lib/battle/BattleHex.h +++ b/lib/battle/BattleHex.h @@ -9,19 +9,12 @@ */ #pragma once +#include "BattleSide.h" + VCMI_LIB_NAMESPACE_BEGIN //TODO: change to enum class -namespace BattleSide -{ - enum Type - { - ATTACKER = 0, - DEFENDER = 1 - }; -} - namespace GameConstants { const int BFIELD_WIDTH = 17; @@ -29,8 +22,6 @@ namespace GameConstants const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; } -using BattleSideOpt = std::optional; - // for battle stacks' positions struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design { @@ -102,7 +93,7 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f static EDir mutualPosition(BattleHex hex1, BattleHex hex2); static uint8_t getDistance(BattleHex hex1, BattleHex hex2); static void checkAndPush(BattleHex tile, std::vector & ret); - static BattleHex getClosestTile(ui8 side, BattleHex initialPos, std::set & possibilities); //TODO: vector or set? copying one to another is bad + static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, std::set & possibilities); //TODO: vector or set? copying one to another is bad template void serialize(Handler &h) diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 4f4a1b3cf..4f7367b1b 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -27,10 +27,20 @@ VCMI_LIB_NAMESPACE_BEGIN -///BattleInfo -CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position) +const SideInBattle & BattleInfo::getSide(BattleSide side) const { - PlayerColor owner = sides[side].color; + return sides.at(side); +} + +SideInBattle & BattleInfo::getSide(BattleSide side) +{ + return sides.at(side); +} + +///BattleInfo +CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, BattleSide side, const SlotID & slot, BattleHex position) +{ + PlayerColor owner = getSide(side).color; assert(!owner.isValidPlayer() || (base.armyObj && base.armyObj->tempOwner == owner)); auto * ret = new CStack(&base, owner, id, side, slot); @@ -39,9 +49,9 @@ CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, return ret; } -CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, const SlotID & slot, BattleHex position) +CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & base, BattleSide side, const SlotID & slot, BattleHex position) { - PlayerColor owner = sides[side].color; + PlayerColor owner = getSide(side).color; auto * ret = new CStack(&base, owner, id, side, slot); ret->initialPosition = position; stacks.push_back(ret); @@ -50,7 +60,7 @@ CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & void BattleInfo::localInit() { - for(int i = 0; i < 2; i++) + for(BattleSide i : { BattleSide::ATTACKER, BattleSide::DEFENDER}) { auto * armyObj = battleGetArmyObject(i); armyObj->battle = this; @@ -162,12 +172,12 @@ struct RangeGenerator std::function myRand; }; -BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town) +BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, BattleSideArray armies, BattleSideArray heroes, bool creatureBank, const CGTownInstance * town) { CMP_stack cmpst; auto * curB = new BattleInfo(); - for(auto i = 0u; i < curB->sides.size(); i++) + for(auto i : { BattleSide::LEFT_SIDE, BattleSide::RIGHT_SIDE}) curB->sides[i].init(heroes[i], armies[i]); @@ -315,36 +325,32 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const //reading battleStartpos - add creatures AFTER random obstacles are generated //TODO: parse once to some structure - std::vector> looseFormations[2]; - std::vector> tightFormations[2]; - std::vector> creBankFormations[2]; - std::vector commanderField; - std::vector commanderBank; + BattleSideArray>> looseFormations; + BattleSideArray>> tightFormations; + BattleSideArray>> creBankFormations; + BattleSideArray commanderField; + BattleSideArray commanderBank; const JsonNode config(JsonPath::builtin("config/battleStartpos.json")); const JsonVector &positions = config["battle_positions"].Vector(); - CGH::readBattlePositions(positions[0]["levels"], looseFormations[0]); - CGH::readBattlePositions(positions[1]["levels"], looseFormations[1]); - CGH::readBattlePositions(positions[2]["levels"], tightFormations[0]); - CGH::readBattlePositions(positions[3]["levels"], tightFormations[1]); - CGH::readBattlePositions(positions[4]["levels"], creBankFormations[0]); - CGH::readBattlePositions(positions[5]["levels"], creBankFormations[1]); + CGH::readBattlePositions(positions[0]["levels"], looseFormations[BattleSide::ATTACKER]); + CGH::readBattlePositions(positions[1]["levels"], looseFormations[BattleSide::DEFENDER]); + CGH::readBattlePositions(positions[2]["levels"], tightFormations[BattleSide::ATTACKER]); + CGH::readBattlePositions(positions[3]["levels"], tightFormations[BattleSide::DEFENDER]); + CGH::readBattlePositions(positions[4]["levels"], creBankFormations[BattleSide::ATTACKER]); + CGH::readBattlePositions(positions[5]["levels"], creBankFormations[BattleSide::DEFENDER]); - for (auto position : config["commanderPositions"]["field"].Vector()) - { - commanderField.push_back(static_cast(position.Float())); - } - for (auto position : config["commanderPositions"]["creBank"].Vector()) - { - commanderBank.push_back(static_cast(position.Float())); - } + commanderField[BattleSide::ATTACKER] = config["commanderPositions"]["field"][0].Integer(); + commanderField[BattleSide::DEFENDER] = config["commanderPositions"]["field"][1].Integer(); + commanderBank[BattleSide::ATTACKER] = config["commanderPositions"]["creBank"][0].Integer(); + commanderBank[BattleSide::DEFENDER] = config["commanderPositions"]["creBank"][1].Integer(); //adding war machines if(!creatureBank) { //Checks if hero has artifact and create appropriate stack - auto handleWarMachine = [&](int side, const ArtifactPosition & artslot, BattleHex hex) + auto handleWarMachine = [&](BattleSide side, const ArtifactPosition & artslot, BattleHex hex) { const CArtifactInstance * warMachineArt = heroes[side]->getArt(artslot); @@ -357,28 +363,28 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const } }; - if(heroes[0]) + if(heroes[BattleSide::ATTACKER]) { - handleWarMachine(0, ArtifactPosition::MACH1, 52); - handleWarMachine(0, ArtifactPosition::MACH2, 18); - handleWarMachine(0, ArtifactPosition::MACH3, 154); + handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH1, 52); + handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH2, 18); + handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH3, 154); if(town && town->hasFort()) - handleWarMachine(0, ArtifactPosition::MACH4, 120); + handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH4, 120); } - if(heroes[1]) + if(heroes[BattleSide::DEFENDER]) { if(!town) //defending hero shouldn't receive ballista (bug #551) - handleWarMachine(1, ArtifactPosition::MACH1, 66); - handleWarMachine(1, ArtifactPosition::MACH2, 32); - handleWarMachine(1, ArtifactPosition::MACH3, 168); + handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH1, 66); + handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH2, 32); + handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH3, 168); } } //war machines added //battleStartpos read - for(int side = 0; side < 2; side++) + for(BattleSide side : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { int formationNo = armies[side]->stacksCount() - 1; vstd::abetween(formationNo, 0, GameConstants::ARMY_SIZE - 1); @@ -397,14 +403,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleHex pos = (k < formationVector->size() ? formationVector->at(k) : 0); if(creatureBank && i->second->type->isDoubleWide()) - pos += side ? BattleHex::LEFT : BattleHex::RIGHT; + pos += side == BattleSide::RIGHT_SIDE ? BattleHex::LEFT : BattleHex::RIGHT; curB->generateNewStack(curB->nextUnitId(), *i->second, side, i->first, pos); } } //adding commanders - for (int i = 0; i < 2; ++i) + for(BattleSide i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive) { @@ -416,14 +422,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL) { // keep tower - curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER); + curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER); if (curB->town->fortLevel() >= CGTownInstance::CASTLE) { // lower tower + upper tower - curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER); + curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER); - curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER); + curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER); } //Moat generating is done on server @@ -453,11 +459,9 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const //tactics bool isTacticsAllowed = !creatureBank; //no tactics in creature banks - constexpr int sideSize = 2; - - std::array battleRepositionHex = {}; - std::array battleRepositionHexBlock = {}; - for(int i = 0; i < sideSize; i++) + BattleSideArray battleRepositionHex = {}; + BattleSideArray battleRepositionHexBlock = {}; + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { if(heroes[i]) { @@ -508,14 +512,14 @@ const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const return nullptr; } -ui8 BattleInfo::whatSide(const PlayerColor & player) const +BattleSide BattleInfo::whatSide(const PlayerColor & player) const { - for(int i = 0; i < sides.size(); i++) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) if(sides[i].color == player) return i; logGlobal->warn("BattleInfo::whatSide: Player %s is not in battle!", player.toString()); - return -1; + return BattleSide::NONE; } CStack * BattleInfo::getStack(int stackID, bool onlyAlive) @@ -529,7 +533,7 @@ BattleInfo::BattleInfo(): town(nullptr), tile(-1,-1,-1), battlefieldType(BattleField::NONE), - tacticsSide(0), + tacticsSide(BattleSide::NONE), tacticDistance(0) { setNodeType(BATTLE); @@ -555,7 +559,7 @@ BattleInfo::~BattleInfo() for (auto & elem : stacks) delete elem; - for(int i = 0; i < 2; i++) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) if(auto * _armyObj = battleGetArmyObject(i)) _armyObj->battle = nullptr; } @@ -600,27 +604,27 @@ IBattleInfo::ObstacleCList BattleInfo::getAllObstacles() const return ret; } -PlayerColor BattleInfo::getSidePlayer(ui8 side) const +PlayerColor BattleInfo::getSidePlayer(BattleSide side) const { - return sides.at(side).color; + return getSide(side).color; } -const CArmedInstance * BattleInfo::getSideArmy(ui8 side) const +const CArmedInstance * BattleInfo::getSideArmy(BattleSide side) const { - return sides.at(side).armyObject; + return getSide(side).armyObject; } -const CGHeroInstance * BattleInfo::getSideHero(ui8 side) const +const CGHeroInstance * BattleInfo::getSideHero(BattleSide side) const { - return sides.at(side).hero; + return getSide(side).hero; } -ui8 BattleInfo::getTacticDist() const +uint8_t BattleInfo::getTacticDist() const { return tacticDistance; } -ui8 BattleInfo::getTacticsSide() const +BattleSide BattleInfo::getTacticsSide() const { return tacticsSide; } @@ -640,14 +644,14 @@ EGateState BattleInfo::getGateState() const return si.gateState; } -uint32_t BattleInfo::getCastSpells(ui8 side) const +uint32_t BattleInfo::getCastSpells(BattleSide side) const { - return sides.at(side).castSpellsCount; + return getSide(side).castSpellsCount; } -int32_t BattleInfo::getEnchanterCounter(ui8 side) const +int32_t BattleInfo::getEnchanterCounter(BattleSide side) const { - return sides.at(side).enchanterCounter; + return getSide(side).enchanterCounter; } const IBonusBearer * BattleInfo::getBonusBearer() const @@ -685,14 +689,14 @@ bool BattleInfo::isCreatureBank() const } -std::vector BattleInfo::getUsedSpells(ui8 side) const +std::vector BattleInfo::getUsedSpells(BattleSide side) const { - return sides.at(side).usedSpellsHistory; + return getSide(side).usedSpellsHistory; } void BattleInfo::nextRound() { - for(int i = 0; i < 2; ++i) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { sides.at(i).castSpellsCount = 0; vstd::amax(--sides.at(i).enchanterCounter, 0); @@ -994,12 +998,12 @@ void BattleInfo::removeObstacle(uint32_t id) } } -CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const +CArmedInstance * BattleInfo::battleGetArmyObject(BattleSide side) const { return const_cast(CBattleInfoEssentials::battleGetArmyObject(side)); } -CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const +CGHeroInstance * BattleInfo::battleGetFightingHero(BattleSide side) const { return const_cast(CBattleInfoEssentials::battleGetFightingHero(side)); } @@ -1009,7 +1013,7 @@ scripting::Pool * BattleInfo::getContextPool() const { //this is real battle, use global scripting context pool //TODO: make this line not ugly - return battleGetFightingHero(0)->cb->getGlobalContextPool(); + return battleGetFightingHero(BattleSide::ATTACKER)->cb->getGlobalContextPool(); } #endif @@ -1045,7 +1049,7 @@ bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) const return false; } -CMP_stack::CMP_stack(int Phase, int Turn, uint8_t Side): +CMP_stack::CMP_stack(int Phase, int Turn, BattleSide Side): phase(Phase), turn(Turn), side(Side) diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index 58b901fab..a95c154b5 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -25,15 +25,10 @@ class BattleField; class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback, public IBattleState { + BattleSideArray sides; //sides[0] - attacker, sides[1] - defender public: BattleID battleID = BattleID(0); - enum BattleSide - { - ATTACKER = 0, - DEFENDER - }; - std::array sides; //sides[0] - attacker, sides[1] - defender si32 round; si32 activeStack; const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege) @@ -47,7 +42,7 @@ public: BattleField battlefieldType; //like !!BA:B TerrainId terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy) - ui8 tacticsSide; //which side is requested to play tactics phase + BattleSide tacticsSide; //which side is requested to play tactics phase ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line) template void serialize(Handler &h) @@ -92,19 +87,19 @@ public: ObstacleCList getAllObstacles() const override; - PlayerColor getSidePlayer(ui8 side) const override; - const CArmedInstance * getSideArmy(ui8 side) const override; - const CGHeroInstance * getSideHero(ui8 side) const override; + PlayerColor getSidePlayer(BattleSide side) const override; + const CArmedInstance * getSideArmy(BattleSide side) const override; + const CGHeroInstance * getSideHero(BattleSide side) const override; ui8 getTacticDist() const override; - ui8 getTacticsSide() const override; + BattleSide getTacticsSide() const override; const CGTownInstance * getDefendedTown() const override; EWallState getWallState(EWallPart partOfWall) const override; EGateState getGateState() const override; - uint32_t getCastSpells(ui8 side) const override; - int32_t getEnchanterCounter(ui8 side) const override; + uint32_t getCastSpells(BattleSide side) const override; + int32_t getEnchanterCounter(BattleSide side) const override; const IBonusBearer * getBonusBearer() const override; @@ -115,7 +110,7 @@ public: int3 getLocation() const override; bool isCreatureBank() const override; - std::vector getUsedSpells(ui8 side) const override; + std::vector getUsedSpells(BattleSide side) const override; ////////////////////////////////////////////////////////////////////////// // IBattleState @@ -144,19 +139,22 @@ public: ////////////////////////////////////////////////////////////////////////// CStack * getStack(int stackID, bool onlyAlive = true); using CBattleInfoEssentials::battleGetArmyObject; - CArmedInstance * battleGetArmyObject(ui8 side) const; + CArmedInstance * battleGetArmyObject(BattleSide side) const; using CBattleInfoEssentials::battleGetFightingHero; - CGHeroInstance * battleGetFightingHero(ui8 side) const; + CGHeroInstance * battleGetFightingHero(BattleSide side) const; - CStack * generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position); - CStack * generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, const SlotID & slot, BattleHex position); + CStack * generateNewStack(uint32_t id, const CStackInstance & base, BattleSide side, const SlotID & slot, BattleHex position); + CStack * generateNewStack(uint32_t id, const CStackBasicDescriptor & base, BattleSide side, const SlotID & slot, BattleHex position); + + const SideInBattle & getSide(BattleSide side) const; + SideInBattle & getSide(BattleSide side); const CGHeroInstance * getHero(const PlayerColor & player) const; //returns fighting hero that belongs to given player void localInit(); - static BattleInfo * setupBattle(const int3 & tile, TerrainId, const BattleField & battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town); + static BattleInfo * setupBattle(const int3 & tile, TerrainId, const BattleField & battlefieldType, BattleSideArray armies, BattleSideArray heroes, bool creatureBank, const CGTownInstance * town); - ui8 whatSide(const PlayerColor & player) const; + BattleSide whatSide(const PlayerColor & player) const; protected: #if SCRIPTING_ENABLED @@ -169,10 +167,10 @@ class DLL_LINKAGE CMP_stack { int phase; //rules of which phase will be used int turn; - uint8_t side; + BattleSide side; public: bool operator()(const battle::Unit * a, const battle::Unit * b) const; - CMP_stack(int Phase = 1, int Turn = 0, uint8_t Side = BattleSide::ATTACKER); + CMP_stack(int Phase = 1, int Turn = 0, BattleSide Side = BattleSide::ATTACKER); }; VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleProxy.cpp b/lib/battle/BattleProxy.cpp index 6bc34a77e..d9a748745 100644 --- a/lib/battle/BattleProxy.cpp +++ b/lib/battle/BattleProxy.cpp @@ -65,17 +65,17 @@ IBattleInfo::ObstacleCList BattleProxy::getAllObstacles() const return subject->battleGetAllObstacles(); } -PlayerColor BattleProxy::getSidePlayer(ui8 side) const +PlayerColor BattleProxy::getSidePlayer(BattleSide side) const { return subject->sideToPlayer(side); } -const CArmedInstance * BattleProxy::getSideArmy(ui8 side) const +const CArmedInstance * BattleProxy::getSideArmy(BattleSide side) const { return subject->battleGetArmyObject(side); } -const CGHeroInstance * BattleProxy::getSideHero(ui8 side) const +const CGHeroInstance * BattleProxy::getSideHero(BattleSide side) const { return subject->battleGetFightingHero(side); } @@ -85,7 +85,7 @@ ui8 BattleProxy::getTacticDist() const return subject->battleTacticDist(); } -ui8 BattleProxy::getTacticsSide() const +BattleSide BattleProxy::getTacticsSide() const { return subject->battleGetTacticsSide(); } @@ -105,12 +105,12 @@ EGateState BattleProxy::getGateState() const return subject->battleGetGateState(); } -uint32_t BattleProxy::getCastSpells(ui8 side) const +uint32_t BattleProxy::getCastSpells(BattleSide side) const { return subject->battleCastSpells(side); } -int32_t BattleProxy::getEnchanterCounter(ui8 side) const +int32_t BattleProxy::getEnchanterCounter(BattleSide side) const { return subject->battleGetEnchanterCounter(side); } diff --git a/lib/battle/BattleProxy.h b/lib/battle/BattleProxy.h index c43ede14d..1131d9614 100644 --- a/lib/battle/BattleProxy.h +++ b/lib/battle/BattleProxy.h @@ -38,19 +38,19 @@ public: ObstacleCList getAllObstacles() const override; - PlayerColor getSidePlayer(ui8 side) const override; - const CArmedInstance * getSideArmy(ui8 side) const override; - const CGHeroInstance * getSideHero(ui8 side) const override; + PlayerColor getSidePlayer(BattleSide side) const override; + const CArmedInstance * getSideArmy(BattleSide side) const override; + const CGHeroInstance * getSideHero(BattleSide side) const override; ui8 getTacticDist() const override; - ui8 getTacticsSide() const override; + BattleSide getTacticsSide() const override; const CGTownInstance * getDefendedTown() const override; EWallState getWallState(EWallPart partOfWall) const override; EGateState getGateState() const override; - uint32_t getCastSpells(ui8 side) const override; - int32_t getEnchanterCounter(ui8 side) const override; + uint32_t getCastSpells(BattleSide side) const override; + int32_t getEnchanterCounter(BattleSide side) const override; const IBonusBearer * getBonusBearer() const override; protected: diff --git a/lib/battle/BattleSide.h b/lib/battle/BattleSide.h new file mode 100644 index 000000000..44156d75c --- /dev/null +++ b/lib/battle/BattleSide.h @@ -0,0 +1,53 @@ +/* + * BattleSide.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 + +VCMI_LIB_NAMESPACE_BEGIN + +enum class BattleSide : int8_t +{ + NONE = -1, + INVALID = -2, + ALL_KNOWING = -3, + + ATTACKER = 0, + DEFENDER = 1, + + // Aliases for convenience + LEFT_SIDE = ATTACKER, + RIGHT_SIDE = DEFENDER, +}; + +template +class BattleSideArray : public std::array +{ +public: + const T & at(BattleSide side) const + { + return std::array::at(static_cast(side)); + } + + T & at(BattleSide side) + { + return std::array::at(static_cast(side)); + } + + const T & operator[](BattleSide side) const + { + return std::array::at(static_cast(side)); + } + + T & operator[](BattleSide side) + { + return std::array::at(static_cast(side)); + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/BattleStateInfoForRetreat.cpp b/lib/battle/BattleStateInfoForRetreat.cpp index 8eabecc12..d517f242a 100644 --- a/lib/battle/BattleStateInfoForRetreat.cpp +++ b/lib/battle/BattleStateInfoForRetreat.cpp @@ -23,7 +23,7 @@ BattleStateInfoForRetreat::BattleStateInfoForRetreat(): isLastTurnBeforeDie(false), ourHero(nullptr), enemyHero(nullptr), - ourSide(-1) + ourSide(BattleSide::NONE) { } diff --git a/lib/battle/BattleStateInfoForRetreat.h b/lib/battle/BattleStateInfoForRetreat.h index 341ed7ffe..42cd8b7a6 100644 --- a/lib/battle/BattleStateInfoForRetreat.h +++ b/lib/battle/BattleStateInfoForRetreat.h @@ -9,6 +9,8 @@ */ #pragma once +#include "BattleSide.h" + VCMI_LIB_NAMESPACE_BEGIN namespace battle @@ -24,7 +26,7 @@ public: bool canFlee; bool canSurrender; bool isLastTurnBeforeDie; - ui8 ourSide; + BattleSide ourSide; std::vector ourStacks; std::vector enemyStacks; const CGHeroInstance * ourHero; diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 9e17542f1..53bec15b1 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -110,9 +110,9 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster * } const PlayerColor player = caster->getCasterOwner(); const auto side = playerToSide(player); - if(!side) + if(side == BattleSide::NONE) return ESpellCastProblem::INVALID; - if(!battleDoWeKnowAbout(side.value())) + if(!battleDoWeKnowAbout(side)) { logGlobal->warn("You can't check if enemy can cast given spell!"); return ESpellCastProblem::INVALID; @@ -125,7 +125,7 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster * { case spells::Mode::HERO: { - if(battleCastSpells(side.value()) > 0) + if(battleCastSpells(side) > 0) return ESpellCastProblem::CASTS_PER_TURN_LIMIT; const auto * hero = dynamic_cast(caster); @@ -173,6 +173,9 @@ bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const { + if (!from.isAvailable() || !dest.isAvailable()) + throw std::runtime_error("Invalid hex (" + std::to_string(from.hex) + " and " + std::to_string(dest.hex) + ") received in battleHasPenaltyOnLine!" ); + auto isTileBlocked = [&](BattleHex tile) { EWallPart wallPart = battleHexToWallPart(tile); @@ -373,7 +376,7 @@ battle::Units CBattleInfoCallback::battleAliveUnits() const }); } -battle::Units CBattleInfoCallback::battleAliveUnits(ui8 side) const +battle::Units CBattleInfoCallback::battleAliveUnits(BattleSide side) const { return battleGetUnitsIf([=](const battle::Unit * unit) { @@ -385,7 +388,7 @@ using namespace battle; //T is battle::Unit descendant template -const T * takeOneUnit(std::vector & allUnits, const int turn, int8_t & sideThatLastMoved, int phase) +const T * takeOneUnit(std::vector & allUnits, const int turn, BattleSide & sideThatLastMoved, int phase) { const T * returnedUnit = nullptr; size_t currentUnitIndex = 0; @@ -414,13 +417,13 @@ const T * takeOneUnit(std::vector & allUnits, const int turn, int8_t & } else if(currentUnitInitiative == returnedUnitInitiative) { - if(sideThatLastMoved == -1 && turn <= 0 && currentUnit->unitSide() == BattleSide::ATTACKER + if(sideThatLastMoved == BattleSide::NONE && turn <= 0 && currentUnit->unitSide() == BattleSide::ATTACKER && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Turn 0 attacker priority { returnedUnit = currentUnit; currentUnitIndex = i; } - else if(sideThatLastMoved != -1 && currentUnit->unitSide() != sideThatLastMoved + else if(sideThatLastMoved != BattleSide::NONE && currentUnit->unitSide() != sideThatLastMoved && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Alternate equal speeds units { returnedUnit = currentUnit; @@ -435,7 +438,7 @@ const T * takeOneUnit(std::vector & allUnits, const int turn, int8_t & returnedUnit = currentUnit; currentUnitIndex = i; } - else if(currentUnitInitiative == returnedUnitInitiative && sideThatLastMoved != -1 && currentUnit->unitSide() != sideThatLastMoved + else if(currentUnitInitiative == returnedUnitInitiative && sideThatLastMoved != BattleSide::NONE && currentUnit->unitSide() != sideThatLastMoved && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Alternate equal speeds units { returnedUnit = currentUnit; @@ -455,7 +458,7 @@ const T * takeOneUnit(std::vector & allUnits, const int turn, int8_t & return returnedUnit; } -void CBattleInfoCallback::battleGetTurnOrder(std::vector & turns, const size_t maxUnits, const int maxTurns, const int turn, int8_t sideThatLastMoved) const +void CBattleInfoCallback::battleGetTurnOrder(std::vector & turns, const size_t maxUnits, const int maxTurns, const int turn, BattleSide sideThatLastMoved) const { RETURN_IF_NOT_BATTLE(); @@ -497,7 +500,7 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & turns, //its first or current turn, turn priority for active stack side //TODO: what if active stack mind-controlled? - if(turn <= 0 && sideThatLastMoved < 0) + if(turn <= 0 && sideThatLastMoved == BattleSide::NONE) sideThatLastMoved = activeUnit->unitSide(); } @@ -557,7 +560,7 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & turns, } } - if(sideThatLastMoved < 0) + if(sideThatLastMoved == BattleSide::NONE) sideThatLastMoved = BattleSide::ATTACKER; if(!turnsIsFull() && (maxTurns == 0 || turns.size() < maxTurns)) @@ -884,7 +887,7 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s spellEnv.apply(&bocp); }; const auto side = unit.unitSide(); - auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side); + auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, side); const auto * hero = battleGetFightingHero(spellObstacle->casterSide); auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle); @@ -1095,7 +1098,7 @@ bool CBattleInfoCallback::isInObstacle( return false; } -std::set CBattleInfoCallback::getStoppers(BattlePerspective::BattlePerspective whichSidePerspective) const +std::set CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const { std::set ret; RETURN_IF_NOT_BATTLE(ret); @@ -1158,7 +1161,7 @@ std::pair CBattleInfoCallback::getNearestStack( return std::make_pair(nullptr, BattleHex::INVALID); } -BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, ui8 side, int initialPos) const +BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos) const { bool twoHex = VLC->creatures()->getById(creID)->isDoubleWide(); @@ -1205,8 +1208,13 @@ bool CBattleInfoCallback::isInTacticRange(BattleHex dest) const auto side = battleGetTacticsSide(); auto dist = battleGetTacticDist(); - return ((!side && dest.getX() > 0 && dest.getX() <= dist) - || (side && dest.getX() < GameConstants::BFIELD_WIDTH - 1 && dest.getX() >= GameConstants::BFIELD_WIDTH - dist - 1)); + if (side == BattleSide::ATTACKER && dest.getX() > 0 && dest.getX() <= dist) + return true; + + if (side == BattleSide::DEFENDER && dest.getX() < GameConstants::BFIELD_WIDTH - 1 && dest.getX() >= GameConstants::BFIELD_WIDTH - dist - 1) + return true; + + return false; } ReachabilityInfo CBattleInfoCallback::getReachability(const battle::Unit * unit) const @@ -1449,7 +1457,7 @@ std::set CBattleInfoCallback::getAttackedCreatures(const CStack* return attackedCres; } -static bool isHexInFront(BattleHex hex, BattleHex testHex, BattleSide::Type side ) +static bool isHexInFront(BattleHex hex, BattleHex testHex, BattleSide side ) { static const std::set rightDirs { BattleHex::BOTTOM_RIGHT, BattleHex::TOP_RIGHT, BattleHex::RIGHT }; static const std::set leftDirs { BattleHex::BOTTOM_LEFT, BattleHex::TOP_LEFT, BattleHex::LEFT }; @@ -1474,7 +1482,7 @@ bool CBattleInfoCallback::isToReverse(const battle::Unit * attacker, const battl if (attackerHex < 0 ) //turret return false; - if(isHexInFront(attackerHex, defenderHex, static_cast(attacker->unitSide()))) + if(isHexInFront(attackerHex, defenderHex, attacker->unitSide())) return false; auto defenderOtherHex = defenderHex; @@ -1484,7 +1492,7 @@ bool CBattleInfoCallback::isToReverse(const battle::Unit * attacker, const battl { defenderOtherHex = battle::Unit::occupiedHex(defenderHex, true, defender->unitSide()); - if(isHexInFront(attackerHex, defenderOtherHex, static_cast(attacker->unitSide()))) + if(isHexInFront(attackerHex, defenderOtherHex, attacker->unitSide())) return false; } @@ -1492,7 +1500,7 @@ bool CBattleInfoCallback::isToReverse(const battle::Unit * attacker, const battl { attackerOtherHex = battle::Unit::occupiedHex(attackerHex, true, attacker->unitSide()); - if(isHexInFront(attackerOtherHex, defenderHex, static_cast(attacker->unitSide()))) + if(isHexInFront(attackerOtherHex, defenderHex, attacker->unitSide())) return false; } @@ -1500,7 +1508,7 @@ bool CBattleInfoCallback::isToReverse(const battle::Unit * attacker, const battl // but this is how H3 handles it which is important, e.g. for direction of dragon breath attacks if (attacker->doubleWide() && defender->doubleWide()) { - if(isHexInFront(attackerOtherHex, defenderOtherHex, static_cast(attacker->unitSide()))) + if(isHexInFront(attackerOtherHex, defenderOtherHex, attacker->unitSide())) return false; } return true; @@ -1759,7 +1767,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(vstd::RNG & rand, const ba case SpellID::PROTECTION_FROM_FIRE: case SpellID::PROTECTION_FROM_WATER: { - const ui8 enemySide = 1 - subject->unitSide(); + const BattleSide enemySide = otherSide(subject->unitSide()); //todo: only if enemy has spellbook if (!battleHasHero(enemySide)) //only if there is enemy hero continue; @@ -1850,10 +1858,9 @@ int CBattleInfoCallback::battleGetSurrenderCost(const PlayerColor & Player) cons if(!battleCanSurrender(Player)) return -1; - const auto sideOpt = playerToSide(Player); - if(!sideOpt) + const BattleSide side = playerToSide(Player); + if(side == BattleSide::NONE) return -1; - const auto side = sideOpt.value(); int ret = 0; double discount = 0; @@ -1869,7 +1876,7 @@ int CBattleInfoCallback::battleGetSurrenderCost(const PlayerColor & Player) cons return ret; } -si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const +si8 CBattleInfoCallback::battleMinSpellLevel(BattleSide side) const { const IBonusBearer * node = nullptr; if(const CGHeroInstance * h = battleGetFightingHero(side)) @@ -1887,7 +1894,7 @@ si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const return 0; } -si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const +si8 CBattleInfoCallback::battleMaxSpellLevel(BattleSide side) const { const IBonusBearer *node = nullptr; if(const CGHeroInstance * h = battleGetFightingHero(side)) @@ -1906,21 +1913,21 @@ si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const return GameConstants::SPELL_LEVELS; } -std::optional CBattleInfoCallback::battleIsFinished() const +std::optional CBattleInfoCallback::battleIsFinished() const { auto units = battleGetUnitsIf([=](const battle::Unit * unit) { return unit->alive() && !unit->isTurret() && !unit->hasBonusOfType(BonusType::SIEGE_WEAPON); }); - std::array hasUnit = {false, false}; //index is BattleSide + BattleSideArray hasUnit = {false, false}; //index is BattleSide for(auto & unit : units) { //todo: move SIEGE_WEAPON check to Unit state hasUnit.at(unit->unitSide()) = true; - if(hasUnit[0] && hasUnit[1]) + if(hasUnit[BattleSide::ATTACKER] && hasUnit[BattleSide::DEFENDER]) return std::nullopt; } @@ -1934,12 +1941,12 @@ std::optional CBattleInfoCallback::battleIsFinished() const } } - if(!hasUnit[0] && !hasUnit[1]) - return 2; - if(!hasUnit[1]) - return 0; + if(!hasUnit[BattleSide::ATTACKER] && !hasUnit[BattleSide::DEFENDER]) + return BattleSide::NONE; + if(!hasUnit[BattleSide::DEFENDER]) + return BattleSide::ATTACKER; else - return 1; + return BattleSide::DEFENDER; } VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index c965e72f5..6e42f3d94 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -56,7 +56,7 @@ struct DLL_LINKAGE BattleClientInterfaceData class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials { public: - std::optional battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw + std::optional battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override; std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set & passed) const override; @@ -70,9 +70,9 @@ public: ///returns all alive units excluding turrets battle::Units battleAliveUnits() const; ///returns all alive units from particular side excluding turrets - battle::Units battleAliveUnits(ui8 side) const; + battle::Units battleAliveUnits(BattleSide side) const; - void battleGetTurnOrder(std::vector & out, const size_t maxUnits, const int maxTurns, const int turn = 0, int8_t lastMoved = -1) const; + void battleGetTurnOrder(std::vector & out, const size_t maxUnits, const int maxTurns, const int turn = 0, BattleSide lastMoved = BattleSide::NONE) const; ///returns reachable hexes (valid movement destinations), DOES contain stack current position std::vector battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector * attackable) const; @@ -116,8 +116,8 @@ public: bool isWallPartAttackable(EWallPart wallPart) const; // returns true if the wall part is actually attackable, false if not std::vector getAttackableBattleHexes() const; - si8 battleMinSpellLevel(ui8 side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned - si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned + si8 battleMinSpellLevel(BattleSide side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned + si8 battleMaxSpellLevel(BattleSide side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned int32_t battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //returns cost of given spell ESpellCastProblem battleCanCastSpell(const spells::Caster * caster, spells::Mode mode) const; //returns true if there are no general issues preventing from casting a spell @@ -163,12 +163,12 @@ public: AccessibilityInfo getAccessibility(const std::vector & accessibleHexes) const; //given hexes will be marked as accessible std::pair getNearestStack(const battle::Unit * closest) const; - BattleHex getAvailableHex(const CreatureID & creID, ui8 side, int initialPos = -1) const; //find place for adding new stack + BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack protected: ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const; ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const; bool isInObstacle(BattleHex hex, const std::set & obstacles, const ReachabilityInfo::Parameters & params) const; - std::set getStoppers(BattlePerspective::BattlePerspective whichSidePerspective) const; //get hexes with stopping obstacles (quicksands) + std::set getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands) }; VCMI_LIB_NAMESPACE_END diff --git a/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp index f3ee409c0..7fce0ad88 100644 --- a/lib/battle/CBattleInfoEssentials.cpp +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -35,13 +35,13 @@ BattleField CBattleInfoEssentials::battleGetBattlefieldType() const return getBattle()->getBattlefieldType(); } -int32_t CBattleInfoEssentials::battleGetEnchanterCounter(ui8 side) const +int32_t CBattleInfoEssentials::battleGetEnchanterCounter(BattleSide side) const { RETURN_IF_NOT_BATTLE(0); return getBattle()->getEnchanterCounter(side); } -std::vector> CBattleInfoEssentials::battleGetAllObstacles(std::optional perspective) const +std::vector> CBattleInfoEssentials::battleGetAllObstacles(std::optional perspective) const { std::vector > ret; RETURN_IF_NOT_BATTLE(ret); @@ -82,13 +82,13 @@ std::shared_ptr CBattleInfoEssentials::battleGetObstacl return std::shared_ptr(); } -bool CBattleInfoEssentials::battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const +bool CBattleInfoEssentials::battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattleSide side) const { RETURN_IF_NOT_BATTLE(false); - return side == BattlePerspective::ALL_KNOWING || coi.visibleForSide(side, battleHasNativeStack(side)); + return side == BattleSide::ALL_KNOWING || coi.visibleForSide(side, battleHasNativeStack(side)); } -bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const +bool CBattleInfoEssentials::battleHasNativeStack(BattleSide side) const { RETURN_IF_NOT_BATTLE(false); @@ -160,18 +160,18 @@ const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const return getBattle()->getDefendedTown(); } -BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const +BattleSide CBattleInfoEssentials::battleGetMySide() const { - RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID); + RETURN_IF_NOT_BATTLE(BattleSide::INVALID); if(!getPlayerID() || getPlayerID()->isSpectator()) - return BattlePerspective::ALL_KNOWING; + return BattleSide::ALL_KNOWING; if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::ATTACKER)) - return BattlePerspective::LEFT_SIDE; + return BattleSide::LEFT_SIDE; if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::DEFENDER)) - return BattlePerspective::RIGHT_SIDE; + return BattleSide::RIGHT_SIDE; logGlobal->error("Cannot find player %s in battle!", getPlayerID()->toString()); - return BattlePerspective::INVALID; + return BattleSide::INVALID; } const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) const @@ -189,11 +189,11 @@ const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) return stacks[0]; } -bool CBattleInfoEssentials::battleDoWeKnowAbout(ui8 side) const +bool CBattleInfoEssentials::battleDoWeKnowAbout(BattleSide side) const { RETURN_IF_NOT_BATTLE(false); auto p = battleGetMySide(); - return p == BattlePerspective::ALL_KNOWING || p == side; + return p == BattleSide::ALL_KNOWING || p == side; } si8 CBattleInfoEssentials::battleTacticDist() const @@ -202,16 +202,16 @@ si8 CBattleInfoEssentials::battleTacticDist() const return getBattle()->getTacticDist(); } -si8 CBattleInfoEssentials::battleGetTacticsSide() const +BattleSide CBattleInfoEssentials::battleGetTacticsSide() const { - RETURN_IF_NOT_BATTLE(-1); + RETURN_IF_NOT_BATTLE(BattleSide::NONE); return getBattle()->getTacticsSide(); } -const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) const +const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(BattleSide side) const { RETURN_IF_NOT_BATTLE(nullptr); - if(side > 1) + if(side != BattleSide::DEFENDER && side != BattleSide::ATTACKER) { logGlobal->error("FIXME: %s wrong argument!", __FUNCTION__); return nullptr; @@ -226,10 +226,10 @@ const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) co return getBattle()->getSideHero(side); } -const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) const +const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(BattleSide side) const { RETURN_IF_NOT_BATTLE(nullptr); - if(side > 1) + if(side != BattleSide::DEFENDER && side != BattleSide::ATTACKER) { logGlobal->error("FIXME: %s wrong argument!", __FUNCTION__); return nullptr; @@ -242,7 +242,7 @@ const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) cons return getBattle()->getSideArmy(side); } -InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo(ui8 side) const +InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo(BattleSide side) const { const auto * hero = getBattle()->getSideHero(side); if(!hero) @@ -253,7 +253,7 @@ InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo(ui8 side) const return InfoAboutHero(hero, infoLevel); } -uint32_t CBattleInfoEssentials::battleCastSpells(ui8 side) const +uint32_t CBattleInfoEssentials::battleCastSpells(BattleSide side) const { RETURN_IF_NOT_BATTLE(-1); return getBattle()->getCastSpells(side); @@ -268,10 +268,10 @@ bool CBattleInfoEssentials::battleCanFlee(const PlayerColor & player) const { RETURN_IF_NOT_BATTLE(false); const auto side = playerToSide(player); - if(!side) + if(side == BattleSide::NONE) return false; - const CGHeroInstance * myHero = battleGetFightingHero(side.value()); + const CGHeroInstance * myHero = battleGetFightingHero(side); //current player have no hero if(!myHero) @@ -292,28 +292,28 @@ bool CBattleInfoEssentials::battleCanFlee(const PlayerColor & player) const return true; } -BattleSideOpt CBattleInfoEssentials::playerToSide(const PlayerColor & player) const +BattleSide CBattleInfoEssentials::playerToSide(const PlayerColor & player) const { - RETURN_IF_NOT_BATTLE(std::nullopt); + RETURN_IF_NOT_BATTLE(BattleSide::NONE); if(getBattle()->getSidePlayer(BattleSide::ATTACKER) == player) - return BattleSideOpt(BattleSide::ATTACKER); + return BattleSide::ATTACKER; if(getBattle()->getSidePlayer(BattleSide::DEFENDER) == player) - return BattleSideOpt(BattleSide::DEFENDER); + return BattleSide::DEFENDER; logGlobal->warn("Cannot find side for player %s", player.toString()); - return std::nullopt; + return BattleSide::INVALID; } -PlayerColor CBattleInfoEssentials::sideToPlayer(ui8 side) const +PlayerColor CBattleInfoEssentials::sideToPlayer(BattleSide side) const { RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE); return getBattle()->getSidePlayer(side); } -ui8 CBattleInfoEssentials::otherSide(ui8 side) const +BattleSide CBattleInfoEssentials::otherSide(BattleSide side) { if(side == BattleSide::ATTACKER) return BattleSide::DEFENDER; @@ -326,19 +326,19 @@ PlayerColor CBattleInfoEssentials::otherPlayer(const PlayerColor & player) const RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE); auto side = playerToSide(player); - if(!side) + if(side == BattleSide::NONE) return PlayerColor::CANNOT_DETERMINE; - return getBattle()->getSidePlayer(otherSide(side.value())); + return getBattle()->getSidePlayer(otherSide(side)); } bool CBattleInfoEssentials::playerHasAccessToHeroInfo(const PlayerColor & player, const CGHeroInstance * h) const { RETURN_IF_NOT_BATTLE(false); const auto side = playerToSide(player); - if(side) + if(side != BattleSide::NONE) { - auto opponentSide = otherSide(side.value()); + auto opponentSide = otherSide(side); if(getBattle()->getSideHero(opponentSide) == h) return true; } @@ -355,14 +355,14 @@ bool CBattleInfoEssentials::battleCanSurrender(const PlayerColor & player) const { RETURN_IF_NOT_BATTLE(false); const auto side = playerToSide(player); - if(!side) + if(side == BattleSide::NONE) return false; - bool iAmSiegeDefender = (side.value() == BattleSide::DEFENDER && getBattle()->getDefendedTown() != nullptr); + bool iAmSiegeDefender = (side == BattleSide::DEFENDER && getBattle()->getDefendedTown() != nullptr); //conditions like for fleeing (except escape tunnel presence) + enemy must have a hero - return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(otherSide(side.value())); + return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(otherSide(side)); } -bool CBattleInfoEssentials::battleHasHero(ui8 side) const +bool CBattleInfoEssentials::battleHasHero(BattleSide side) const { RETURN_IF_NOT_BATTLE(false); return getBattle()->getSideHero(side) != nullptr; @@ -413,9 +413,9 @@ const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const battle::U { RETURN_IF_NOT_BATTLE(nullptr); const auto side = playerToSide(battleGetOwner(unit)); - if(!side) + if(side == BattleSide::NONE) return nullptr; - return getBattle()->getSideHero(side.value()); + return getBattle()->getSideHero(side); } bool CBattleInfoEssentials::battleMatchOwner(const battle::Unit * attacker, const battle::Unit * defender, const boost::logic::tribool positivness) const diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index 3af7c1b50..c389bff74 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -9,6 +9,7 @@ */ #pragma once #include "IBattleInfoCallback.h" +#include "BattleSide.h" VCMI_LIB_NAMESPACE_BEGIN @@ -22,21 +23,10 @@ class CArmedInstance; using TStacks = std::vector; using TStackFilter = std::function; -namespace BattlePerspective -{ - enum BattlePerspective - { - INVALID = -2, - ALL_KNOWING = -1, - LEFT_SIDE, - RIGHT_SIDE - }; -} - class DLL_LINKAGE CBattleInfoEssentials : public IBattleInfoCallback { protected: - bool battleDoWeKnowAbout(ui8 side) const; + bool battleDoWeKnowAbout(BattleSide side) const; public: enum EStackOwnership @@ -45,14 +35,14 @@ public: }; bool duringBattle() const; - BattlePerspective::BattlePerspective battleGetMySide() const; + BattleSide battleGetMySide() const; const IBonusBearer * getBonusBearer() const override; TerrainId battleTerrainType() const override; BattleField battleGetBattlefieldType() const override; - int32_t battleGetEnchanterCounter(ui8 side) const; + int32_t battleGetEnchanterCounter(BattleSide side) const; - std::vector> battleGetAllObstacles(std::optional perspective = std::nullopt) const; //returns all obstacles on the battlefield + std::vector> battleGetAllObstacles(std::optional perspective = std::nullopt) const; //returns all obstacles on the battlefield std::shared_ptr battleGetObstacleByID(uint32_t ID) const; @@ -70,27 +60,27 @@ public: uint32_t battleNextUnitId() const override; - bool battleHasNativeStack(ui8 side) const; + bool battleHasNativeStack(BattleSide side) const; const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead si8 battleTacticDist() const override; //returns tactic distance in current tactics phase; 0 if not in tactics phase - si8 battleGetTacticsSide() const override; //returns which side is in tactics phase, undefined if none (?) + BattleSide battleGetTacticsSide() const override; //returns which side is in tactics phase, undefined if none (?) bool battleCanFlee(const PlayerColor & player) const; bool battleCanSurrender(const PlayerColor & player) const; - ui8 otherSide(ui8 side) const; + static BattleSide otherSide(BattleSide side); PlayerColor otherPlayer(const PlayerColor & player) const; - BattleSideOpt playerToSide(const PlayerColor & player) const; - PlayerColor sideToPlayer(ui8 side) const; + BattleSide playerToSide(const PlayerColor & player) const; + PlayerColor sideToPlayer(BattleSide side) const; bool playerHasAccessToHeroInfo(const PlayerColor & player, const CGHeroInstance * h) const; ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle - bool battleHasHero(ui8 side) const; - uint32_t battleCastSpells(ui8 side) const; //how many spells has given side cast - const CGHeroInstance * battleGetFightingHero(ui8 side) const; //deprecated for players callback, easy to get wrong - const CArmedInstance * battleGetArmyObject(ui8 side) const; - InfoAboutHero battleGetHeroInfo(ui8 side) const; + bool battleHasHero(BattleSide side) const; + uint32_t battleCastSpells(BattleSide side) const; //how many spells has given side cast + const CGHeroInstance * battleGetFightingHero(BattleSide side) const; //deprecated for players callback, easy to get wrong + const CArmedInstance * battleGetArmyObject(BattleSide side) const; + InfoAboutHero battleGetHeroInfo(BattleSide side) const; // for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, // [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle @@ -103,7 +93,7 @@ public: TStacks battleGetAllStacks(bool includeTurrets = false) const; const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID - bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; + bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattleSide side) const; ///returns player that controls given stack; mind control included PlayerColor battleGetOwner(const battle::Unit * unit) const; diff --git a/lib/battle/CObstacleInstance.cpp b/lib/battle/CObstacleInstance.cpp index ae716943c..098d980a2 100644 --- a/lib/battle/CObstacleInstance.cpp +++ b/lib/battle/CObstacleInstance.cpp @@ -52,7 +52,7 @@ std::vector CObstacleInstance::getAffectedTiles() const } } -bool CObstacleInstance::visibleForSide(ui8 side, bool hasNativeStack) const +bool CObstacleInstance::visibleForSide(BattleSide side, bool hasNativeStack) const { //by default obstacle is visible for everyone return true; @@ -134,7 +134,7 @@ SpellCreatedObstacle::SpellCreatedObstacle() : turnsRemaining(-1), casterSpellPower(0), spellLevel(0), - casterSide(0), + casterSide(BattleSide::NONE), hidden(false), passable(false), trigger(false), @@ -148,7 +148,7 @@ SpellCreatedObstacle::SpellCreatedObstacle() obstacleType = SPELL_CREATED; } -bool SpellCreatedObstacle::visibleForSide(ui8 side, bool hasNativeStack) const +bool SpellCreatedObstacle::visibleForSide(BattleSide side, bool hasNativeStack) const { //we hide mines and not discovered quicksands //quicksands are visible to the caster or if owned unit stepped into that particular patch diff --git a/lib/battle/CObstacleInstance.h b/lib/battle/CObstacleInstance.h index 8499f9bb5..3ce98a202 100644 --- a/lib/battle/CObstacleInstance.h +++ b/lib/battle/CObstacleInstance.h @@ -50,7 +50,7 @@ struct DLL_LINKAGE CObstacleInstance : public Serializeable virtual SpellID getTrigger() const; virtual std::vector getAffectedTiles() const; - virtual bool visibleForSide(ui8 side, bool hasNativeStack) const; //0 attacker + virtual bool visibleForSide(BattleSide side, bool hasNativeStack) const; //0 attacker virtual void battleTurnPassed(){}; @@ -80,7 +80,7 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance int32_t casterSpellPower; int32_t spellLevel; int32_t minimalDamage; //How many damage should it do regardless of power and level of caster - si8 casterSide; //0 - obstacle created by attacker; 1 - by defender + BattleSide casterSide; SpellID trigger; @@ -102,7 +102,7 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance SpellCreatedObstacle(); std::vector getAffectedTiles() const override; - bool visibleForSide(ui8 side, bool hasNativeStack) const override; + bool visibleForSide(BattleSide side, bool hasNativeStack) const override; bool blocksTiles() const override; bool stopsMovement() const override; diff --git a/lib/battle/CPlayerBattleCallback.cpp b/lib/battle/CPlayerBattleCallback.cpp index c0457b89e..2de755613 100644 --- a/lib/battle/CPlayerBattleCallback.cpp +++ b/lib/battle/CPlayerBattleCallback.cpp @@ -76,7 +76,7 @@ const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const InfoAboutHero CPlayerBattleCallback::battleGetEnemyHero() const { - return battleGetHeroInfo(!battleGetMySide()); + return battleGetHeroInfo(otherSide(battleGetMySide())); } diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index c96dc721c..e00f5925b 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -921,7 +921,7 @@ uint32_t CUnitStateDetached::unitId() const return unit->unitId(); } -ui8 CUnitStateDetached::unitSide() const +BattleSide CUnitStateDetached::unitSide() const { return unit->unitSide(); } diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h index c641d1a0a..09d8dba7b 100644 --- a/lib/battle/CUnitState.h +++ b/lib/battle/CUnitState.h @@ -289,7 +289,7 @@ public: CUnitStateDetached & operator= (const CUnitState & other); uint32_t unitId() const override; - ui8 unitSide() const override; + BattleSide unitSide() const override; const CCreature * unitType() const override; PlayerColor unitOwner() const override; diff --git a/lib/battle/IBattleInfoCallback.h b/lib/battle/IBattleInfoCallback.h index b5b9ce583..ca43cb767 100644 --- a/lib/battle/IBattleInfoCallback.h +++ b/lib/battle/IBattleInfoCallback.h @@ -65,10 +65,10 @@ public: virtual BattleField battleGetBattlefieldType() const = 0; ///return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw - virtual std::optional battleIsFinished() const = 0; + virtual std::optional battleIsFinished() const = 0; virtual si8 battleTacticDist() const = 0; //returns tactic distance in current tactics phase; 0 if not in tactics phase - virtual si8 battleGetTacticsSide() const = 0; //returns which side is in tactics phase, undefined if none (?) + virtual BattleSide battleGetTacticsSide() const = 0; //returns which side is in tactics phase, undefined if none (?) virtual uint32_t battleNextUnitId() const = 0; diff --git a/lib/battle/IBattleState.h b/lib/battle/IBattleState.h index fd643c56c..6c866d449 100644 --- a/lib/battle/IBattleState.h +++ b/lib/battle/IBattleState.h @@ -55,17 +55,17 @@ public: virtual EWallState getWallState(EWallPart partOfWall) const = 0; virtual EGateState getGateState() const = 0; - virtual PlayerColor getSidePlayer(ui8 side) const = 0; - virtual const CArmedInstance * getSideArmy(ui8 side) const = 0; - virtual const CGHeroInstance * getSideHero(ui8 side) const = 0; + virtual PlayerColor getSidePlayer(BattleSide side) const = 0; + virtual const CArmedInstance * getSideArmy(BattleSide side) const = 0; + virtual const CGHeroInstance * getSideHero(BattleSide side) const = 0; /// Returns list of all spells used by specified side (and that can be learned by opposite hero) - virtual std::vector getUsedSpells(ui8 side) const = 0; + virtual std::vector getUsedSpells(BattleSide side) const = 0; - virtual uint32_t getCastSpells(ui8 side) const = 0; - virtual int32_t getEnchanterCounter(ui8 side) const = 0; + virtual uint32_t getCastSpells(BattleSide side) const = 0; + virtual int32_t getEnchanterCounter(BattleSide side) const = 0; virtual ui8 getTacticDist() const = 0; - virtual ui8 getTacticsSide() const = 0; + virtual BattleSide getTacticsSide() const = 0; virtual uint32_t nextUnitId() const = 0; diff --git a/lib/battle/IUnitInfo.h b/lib/battle/IUnitInfo.h index f91ccce2f..3185b84f1 100644 --- a/lib/battle/IUnitInfo.h +++ b/lib/battle/IUnitInfo.h @@ -11,6 +11,7 @@ #pragma once #include "../GameConstants.h" +#include "BattleSide.h" VCMI_LIB_NAMESPACE_BEGIN @@ -35,7 +36,7 @@ public: virtual int32_t unitBaseAmount() const = 0; virtual uint32_t unitId() const = 0; - virtual ui8 unitSide() const = 0; + virtual BattleSide unitSide() const = 0; virtual PlayerColor unitOwner() const = 0; virtual SlotID unitSlot() const = 0; diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp index 809c3450d..c0165b7af 100644 --- a/lib/battle/ReachabilityInfo.cpp +++ b/lib/battle/ReachabilityInfo.cpp @@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN ReachabilityInfo::Parameters::Parameters(const battle::Unit * Stack, BattleHex StartPosition): - perspective(static_cast(Stack->unitSide())), + perspective(static_cast(Stack->unitSide())), startPosition(StartPosition), doubleWide(Stack->doubleWide()), side(Stack->unitSide()), diff --git a/lib/battle/ReachabilityInfo.h b/lib/battle/ReachabilityInfo.h index 7c872ad86..de4fe21c4 100644 --- a/lib/battle/ReachabilityInfo.h +++ b/lib/battle/ReachabilityInfo.h @@ -25,14 +25,14 @@ struct DLL_LINKAGE ReachabilityInfo struct DLL_LINKAGE Parameters { - ui8 side = 0; + BattleSide side = BattleSide::NONE; bool doubleWide = false; bool flying = false; bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes std::vector knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself) BattleHex startPosition; //assumed position of stack - BattlePerspective::BattlePerspective perspective = BattlePerspective::ALL_KNOWING; //some obstacles (eg. quicksands) may be invisible for some side + BattleSide perspective = BattleSide::ALL_KNOWING; //some obstacles (eg. quicksands) may be invisible for some side Parameters() = default; Parameters(const battle::Unit * Stack, BattleHex StartPosition); diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index 3ad33e95b..51bf21d77 100644 --- a/lib/battle/Unit.cpp +++ b/lib/battle/Unit.cpp @@ -41,7 +41,7 @@ bool Unit::isTurret() const std::string Unit::getDescription() const { boost::format fmt("Unit %d of side %d"); - fmt % unitId() % unitSide(); + fmt % unitId() % static_cast(unitSide()); return fmt.str(); } @@ -58,7 +58,7 @@ std::vector Unit::getSurroundingHexes(BattleHex assumedPosition) cons return getSurroundingHexes(hex, doubleWide(), unitSide()); } -std::vector Unit::getSurroundingHexes(BattleHex position, bool twoHex, ui8 side) +std::vector Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side) { std::vector hexes; if(twoHex) @@ -135,7 +135,7 @@ std::vector Unit::getHexes(BattleHex assumedPos) const return getHexes(assumedPos, doubleWide(), unitSide()); } -std::vector Unit::getHexes(BattleHex assumedPos, bool twoHex, ui8 side) +std::vector Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side) { std::vector hexes; hexes.push_back(assumedPos); @@ -156,7 +156,7 @@ BattleHex Unit::occupiedHex(BattleHex assumedPos) const return occupiedHex(assumedPos, doubleWide(), unitSide()); } -BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, ui8 side) +BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side) { if(twoHex) { diff --git a/lib/battle/Unit.h b/lib/battle/Unit.h index aa42fedd7..cbe585593 100644 --- a/lib/battle/Unit.h +++ b/lib/battle/Unit.h @@ -129,17 +129,17 @@ public: std::vector getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size std::vector getAttackableHexes(const Unit * attacker) const; - static std::vector getSurroundingHexes(BattleHex position, bool twoHex, ui8 side); + static std::vector getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side); bool coversPos(BattleHex position) const; //checks also if unit is double-wide std::vector getHexes() const; //up to two occupied hexes, starting from front std::vector getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front - static std::vector getHexes(BattleHex assumedPos, bool twoHex, ui8 side); + static std::vector getHexes(BattleHex assumedPos, bool twoHex, BattleSide side); BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1 - static BattleHex occupiedHex(BattleHex assumedPos, bool twoHex, ui8 side); + static BattleHex occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side); ///MetaStrings void addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural = boost::logic::indeterminate) const; @@ -166,7 +166,7 @@ public: uint32_t id = 0; TQuantity count = 0; CreatureID type; - ui8 side = 0; + BattleSide side = BattleSide::NONE; BattleHex position; bool summoned = false; diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index 1edbdd4ee..9438710ce 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -222,7 +222,7 @@ namespace BonusDuration //when bonus is automatically removed { // We use uint16_t directly because std::bitset<11> eats whole 8 byte word. using Type = uint16_t; - constexpr static size_t Size = 11; + constexpr size_t Size = 11; enum BonusDuration : Type { PERMANENT = 1 << 0, diff --git a/lib/campaign/CampaignHandler.cpp b/lib/campaign/CampaignHandler.cpp index 44dcc763b..f1636e425 100644 --- a/lib/campaign/CampaignHandler.cpp +++ b/lib/campaign/CampaignHandler.cpp @@ -604,15 +604,15 @@ std::vector< std::vector > CampaignHandler::getFile(std::unique_ptr ioApi(new CProxyROIOApi(buffer)); + auto ioApi = std::make_shared(buffer); CZipLoader loader("", "_", ioApi); // load header - JsonPath resource = JsonPath::builtin(VCMP_HEADER_FILE_NAME); - if(!loader.existsResource(resource)) - throw std::runtime_error(resource.getName() + " not found in " + filename); - auto data = loader.load(resource)->readAll(); - ret.push_back(std::vector(data.first.get(), data.first.get() + data.second)); + JsonPath jsonPath = JsonPath::builtin(VCMP_HEADER_FILE_NAME); + if(!loader.existsResource(jsonPath)) + throw std::runtime_error(jsonPath.getName() + " not found in " + filename); + auto data = loader.load(jsonPath)->readAll(); + ret.emplace_back(data.first.get(), data.first.get() + data.second); if(headerOnly) return ret; @@ -621,11 +621,11 @@ std::vector< std::vector > CampaignHandler::getFile(std::unique_ptr(data.first.get()), data.second, VCMP_HEADER_FILE_NAME); for(auto scenario : header["scenarios"].Vector()) { - ResourcePath resource = ResourcePath(scenario["map"].String(), EResType::MAP); - if(!loader.existsResource(resource)) - throw std::runtime_error(resource.getName() + " not found in " + filename); - auto data = loader.load(resource)->readAll(); - ret.push_back(std::vector(data.first.get(), data.first.get() + data.second)); + ResourcePath mapPath(scenario["map"].String(), EResType::MAP); + if(!loader.existsResource(mapPath)) + throw std::runtime_error(mapPath.getName() + " not found in " + filename); + auto data = loader.load(mapPath)->readAll(); + ret.emplace_back(data.first.get(), data.first.get() + data.second); } return ret; diff --git a/lib/campaign/CampaignState.cpp b/lib/campaign/CampaignState.cpp index ba14cfffd..d8236746a 100644 --- a/lib/campaign/CampaignState.cpp +++ b/lib/campaign/CampaignState.cpp @@ -45,7 +45,9 @@ CampaignRegions CampaignRegions::fromJson(const JsonNode & node) { CampaignRegions cr; cr.campPrefix = node["prefix"].String(); - cr.colorSuffixLength = static_cast(node["color_suffix_length"].Float()); + cr.colorSuffixLength = static_cast(node["colorSuffixLength"].Float()); + cr.campSuffix = node["suffix"].isNull() ? std::vector() : std::vector{node["suffix"].Vector()[0].String(), node["suffix"].Vector()[1].String(), node["suffix"].Vector()[2].String()}; + cr.campBackground = node["background"].isNull() ? "" : node["background"].String(); for(const JsonNode & desc : node["desc"].Vector()) cr.regions.push_back(CampaignRegions::RegionDescription::fromJson(desc)); @@ -68,7 +70,10 @@ CampaignRegions CampaignRegions::getLegacy(int campId) ImagePath CampaignRegions::getBackgroundName() const { - return ImagePath::builtin(campPrefix + "_BG.BMP"); + if(campBackground.empty()) + return ImagePath::builtin(campPrefix + "_BG.BMP"); + else + return ImagePath::builtin(campBackground); } Point CampaignRegions::getPosition(CampaignScenarioID which) const @@ -81,30 +86,39 @@ ImagePath CampaignRegions::getNameFor(CampaignScenarioID which, int colorIndex, { auto const & region = regions[which.getNum()]; - static const std::string colors[2][8] = - { - {"R", "B", "N", "G", "O", "V", "T", "P"}, - {"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"} - }; + static const std::array, 3> colors = {{ + { "", "", "", "", "", "", "", "" }, + { "R", "B", "N", "G", "O", "V", "T", "P" }, + { "Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi" } + }}; - std::string color = colors[colorSuffixLength - 1][colorIndex]; + std::string color = colors[colorSuffixLength][colorIndex]; return ImagePath::builtin(campPrefix + region.infix + "_" + type + color + ".BMP"); } ImagePath CampaignRegions::getAvailableName(CampaignScenarioID which, int color) const { - return getNameFor(which, color, "En"); + if(campSuffix.empty()) + return getNameFor(which, color, "En"); + else + return getNameFor(which, color, campSuffix[0]); } ImagePath CampaignRegions::getSelectedName(CampaignScenarioID which, int color) const { - return getNameFor(which, color, "Se"); + if(campSuffix.empty()) + return getNameFor(which, color, "Se"); + else + return getNameFor(which, color, campSuffix[1]); } ImagePath CampaignRegions::getConqueredName(CampaignScenarioID which, int color) const { - return getNameFor(which, color, "Co"); + if(campSuffix.empty()) + return getNameFor(which, color, "Co"); + else + return getNameFor(which, color, campSuffix[2]); } diff --git a/lib/campaign/CampaignState.h b/lib/campaign/CampaignState.h index 6f66aef17..17d91e528 100644 --- a/lib/campaign/CampaignState.h +++ b/lib/campaign/CampaignState.h @@ -33,6 +33,8 @@ class IGameCallback; class DLL_LINKAGE CampaignRegions { std::string campPrefix; + std::vector campSuffix; + std::string campBackground; int colorSuffixLength; struct DLL_LINKAGE RegionDescription @@ -67,6 +69,11 @@ public: h & campPrefix; h & colorSuffixLength; h & regions; + if (h.version >= Handler::Version::CAMPAIGN_REGIONS) + { + h & campSuffix; + h & campBackground; + } } static CampaignRegions fromJson(const JsonNode & node); diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index 58356a826..9b4a69d35 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -280,6 +280,7 @@ public: enum Type { DEFAULT = -50, + HORDE_PLACEHOLDER8 = -37, HORDE_PLACEHOLDER7 = -36, HORDE_PLACEHOLDER6 = -35, HORDE_PLACEHOLDER5 = -34, @@ -297,7 +298,7 @@ public: HORDE_2_UPGR, GRAIL, EXTRA_TOWN_HALL, EXTRA_CITY_HALL, EXTRA_CAPITOL, DWELL_FIRST=30, DWELL_LVL_2, DWELL_LVL_3, DWELL_LVL_4, DWELL_LVL_5, DWELL_LVL_6, DWELL_LAST=36, DWELL_UP_FIRST=37, DWELL_LVL_2_UP, DWELL_LVL_3_UP, DWELL_LVL_4_UP, DWELL_LVL_5_UP, - DWELL_LVL_6_UP, DWELL_UP_LAST=43, + DWELL_LVL_6_UP, DWELL_UP_LAST=43, DWELL_LVL_8=150, DWELL_LVL_8_UP=151, DWELL_LVL_1 = DWELL_FIRST, DWELL_LVL_7 = DWELL_LAST, @@ -313,6 +314,44 @@ public: }; +private: + static std::vector> getDwellings() + { + std::vector dwellings = { DWELL_LVL_1, DWELL_LVL_2, DWELL_LVL_3, DWELL_LVL_4, DWELL_LVL_5, DWELL_LVL_6, DWELL_LVL_7, DWELL_LVL_8 }; + std::vector dwellingsUp = { DWELL_LVL_1_UP, DWELL_LVL_2_UP, DWELL_LVL_3_UP, DWELL_LVL_4_UP, DWELL_LVL_5_UP, DWELL_LVL_6_UP, DWELL_LVL_7_UP, DWELL_LVL_8_UP }; + return {dwellings, dwellingsUp}; + } + +public: + static Type getDwellingFromLevel(int level, int upgradeIndex) + { + return getDwellings()[upgradeIndex][level]; + } + + static int getLevelFromDwelling(BuildingIDBase dwelling) + { + for(int i = 0; i < 2; i++) + { + auto tmp = getDwellings()[i]; + auto it = std::find(tmp.begin(), tmp.end(), dwelling); + if (it != tmp.end()) + return std::distance(tmp.begin(), it); + } + return (dwelling - DWELL_FIRST) % (GameConstants::CREATURES_PER_TOWN - 1); + } + + static int getUpgradedFromDwelling(BuildingIDBase dwelling) + { + for(int i = 0; i < 2; i++) + { + auto tmp = getDwellings()[i]; + auto it = std::find(tmp.begin(), tmp.end(), dwelling); + if (it != tmp.end()) + return i; + } + return (dwelling - DWELL_FIRST) / (GameConstants::CREATURES_PER_TOWN - 1); + } + bool IsSpecialOrGrail() const { return num == SPECIAL_1 || num == SPECIAL_2 || num == SPECIAL_3 || num == SPECIAL_4 || num == GRAIL; diff --git a/lib/constants/NumericConstants.h b/lib/constants/NumericConstants.h index 1e3693649..f8833e640 100644 --- a/lib/constants/NumericConstants.h +++ b/lib/constants/NumericConstants.h @@ -22,7 +22,7 @@ namespace GameConstants constexpr int ALL_PLAYERS = 255; //bitfield - constexpr int CREATURES_PER_TOWN = 7; //without upgrades + constexpr int CREATURES_PER_TOWN = 8; //without upgrades constexpr int SPELL_LEVELS = 5; constexpr int SPELL_SCHOOL_LEVELS = 4; constexpr int DEFAULT_SCHOOLS = 4; diff --git a/lib/constants/StringConstants.h b/lib/constants/StringConstants.h index 962bcb3bf..2fdf9b9d9 100644 --- a/lib/constants/StringConstants.h +++ b/lib/constants/StringConstants.h @@ -56,7 +56,7 @@ namespace NSecondarySkill namespace EBuildingType { - const std::string names [44] = + const std::string names [46] = { "mageGuild1", "mageGuild2", "mageGuild3", "mageGuild4", "mageGuild5", // 5 "tavern", "shipyard", "fort", "citadel", "castle", // 10 @@ -66,7 +66,8 @@ namespace EBuildingType "horde2Upgr", "grail", "extraTownHall", "extraCityHall", "extraCapitol", // 30 "dwellingLvl1", "dwellingLvl2", "dwellingLvl3", "dwellingLvl4", "dwellingLvl5", // 35 "dwellingLvl6", "dwellingLvl7", "dwellingUpLvl1", "dwellingUpLvl2", "dwellingUpLvl3", // 40 - "dwellingUpLvl4", "dwellingUpLvl5", "dwellingUpLvl6", "dwellingUpLvl7" + "dwellingUpLvl4", "dwellingUpLvl5", "dwellingUpLvl6", "dwellingUpLvl7", "dwellingLvl8", + "dwellingUpLvl8" }; } @@ -163,6 +164,7 @@ namespace MappedKeys { "dwellingLvl5", BuildingID::DWELL_LVL_5 }, { "dwellingLvl6", BuildingID::DWELL_LVL_6 }, { "dwellingLvl7", BuildingID::DWELL_LVL_7 }, + { "dwellingLvl8", BuildingID::DWELL_LVL_8 }, { "dwellingUpLvl1", BuildingID::DWELL_LVL_1_UP }, { "dwellingUpLvl2", BuildingID::DWELL_LVL_2_UP }, { "dwellingUpLvl3", BuildingID::DWELL_LVL_3_UP }, @@ -170,6 +172,7 @@ namespace MappedKeys { "dwellingUpLvl5", BuildingID::DWELL_LVL_5_UP }, { "dwellingUpLvl6", BuildingID::DWELL_LVL_6_UP }, { "dwellingUpLvl7", BuildingID::DWELL_LVL_7_UP }, + { "dwellingUpLvl8", BuildingID::DWELL_LVL_8_UP }, }; static const std::map SPECIAL_BUILDINGS = diff --git a/lib/entities/building/CBuilding.cpp b/lib/entities/building/CBuilding.cpp index ed8e88fb4..b9b7d4401 100644 --- a/lib/entities/building/CBuilding.cpp +++ b/lib/entities/building/CBuilding.cpp @@ -33,7 +33,7 @@ const std::map CBuilding::TOWER_TYPES = { "skyship", CBuilding::HEIGHT_SKYSHIP } }; -const BuildingTypeUniqueID CBuilding::getUniqueTypeID() const +BuildingTypeUniqueID CBuilding::getUniqueTypeID() const { return BuildingTypeUniqueID(town->faction->getId(), bid); } diff --git a/lib/entities/building/CBuilding.h b/lib/entities/building/CBuilding.h index 0c30bd951..991326330 100644 --- a/lib/entities/building/CBuilding.h +++ b/lib/entities/building/CBuilding.h @@ -66,7 +66,7 @@ public: CBuilding() : town(nullptr), mode(BUILD_NORMAL) {}; - const BuildingTypeUniqueID getUniqueTypeID() const; + BuildingTypeUniqueID getUniqueTypeID() const; std::string getJsonKey() const; diff --git a/lib/entities/building/CBuildingHandler.cpp b/lib/entities/building/CBuildingHandler.cpp index dcdd1c2ca..a74fe31b9 100644 --- a/lib/entities/building/CBuildingHandler.cpp +++ b/lib/entities/building/CBuildingHandler.cpp @@ -9,6 +9,9 @@ */ #include "StdInc.h" #include "CBuildingHandler.h" +#include "VCMI_Lib.h" +#include "../faction/CTown.h" +#include "../faction/CTownHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -36,7 +39,7 @@ BuildingID CBuildingHandler::campToERMU(int camp, FactionID townType, const std: }; int curPos = static_cast(campToERMU.size()); - for (int i=0; itownh)[townType]->town->creatures.size(); ++i) { if(camp == curPos) //non-upgraded return BuildingID(30 + i); @@ -53,7 +56,7 @@ BuildingID CBuildingHandler::campToERMU(int camp, FactionID townType, const std: { if (hordeLvlsPerTType[townType.getNum()][0] == i) { - BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType.getNum()][0]); + BuildingID dwellingID(BuildingID::getDwellingFromLevel(hordeLvlsPerTType[townType.getNum()][0], 1)); if(vstd::contains(builtBuildings, dwellingID)) //if upgraded dwelling is built return BuildingID::HORDE_1_UPGR; @@ -64,7 +67,7 @@ BuildingID CBuildingHandler::campToERMU(int camp, FactionID townType, const std: { if(hordeLvlsPerTType[townType.getNum()].size() > 1) { - BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType.getNum()][1]); + BuildingID dwellingID(BuildingID::getDwellingFromLevel(hordeLvlsPerTType[townType.getNum()][1], 1)); if(vstd::contains(builtBuildings, dwellingID)) //if upgraded dwelling is built return BuildingID::HORDE_2_UPGR; diff --git a/lib/entities/faction/CFaction.h b/lib/entities/faction/CFaction.h index ff4324ccf..294a77529 100644 --- a/lib/entities/faction/CFaction.h +++ b/lib/entities/faction/CFaction.h @@ -11,6 +11,7 @@ #include +#include "../../Point.h" #include "../../constants/EntityIdentifiers.h" #include "../../constants/Enumerations.h" #include "../../filesystem/ResourcePath.h" @@ -21,8 +22,8 @@ class CTown; struct DLL_LINKAGE SPuzzleInfo { + Point position; ui16 number; //type of puzzle - si16 x, y; //position ui16 whenUncovered; //determines the sequence of discovering (the lesser it is the sooner puzzle will be discovered) ImagePath filename; //file with graphic of this puzzle }; diff --git a/lib/entities/faction/CTownHandler.cpp b/lib/entities/faction/CTownHandler.cpp index 1f2cbd544..0d54cff5d 100644 --- a/lib/entities/faction/CTownHandler.cpp +++ b/lib/entities/faction/CTownHandler.cpp @@ -733,8 +733,8 @@ void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source) const size_t index = faction.puzzleMap.size(); SPuzzleInfo spi; - spi.x = static_cast(piece["x"].Float()); - spi.y = static_cast(piece["y"].Float()); + spi.position.x = static_cast(piece["x"].Float()); + spi.position.y = static_cast(piece["y"].Float()); spi.whenUncovered = static_cast(piece["index"].Float()); spi.number = static_cast(index); diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 696d51308..7598801b6 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -795,10 +795,11 @@ void CGameState::initTowns() for (auto & vti : map->towns) { assert(vti->town); + assert(vti->town->creatures.size() <= GameConstants::CREATURES_PER_TOWN); - constexpr std::array basicDwellings = { BuildingID::DWELL_FIRST, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 }; - constexpr std::array upgradedDwellings = { BuildingID::DWELL_UP_FIRST, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP }; - constexpr std::array hordes = { BuildingID::HORDE_PLACEHOLDER1, BuildingID::HORDE_PLACEHOLDER2, BuildingID::HORDE_PLACEHOLDER3, BuildingID::HORDE_PLACEHOLDER4, BuildingID::HORDE_PLACEHOLDER5, BuildingID::HORDE_PLACEHOLDER6, BuildingID::HORDE_PLACEHOLDER7 }; + constexpr std::array basicDwellings = { BuildingID::DWELL_FIRST, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7, BuildingID::DWELL_LVL_8 }; + constexpr std::array upgradedDwellings = { BuildingID::DWELL_UP_FIRST, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP, BuildingID::DWELL_LVL_8_UP }; + constexpr std::array hordes = { BuildingID::HORDE_PLACEHOLDER1, BuildingID::HORDE_PLACEHOLDER2, BuildingID::HORDE_PLACEHOLDER3, BuildingID::HORDE_PLACEHOLDER4, BuildingID::HORDE_PLACEHOLDER5, BuildingID::HORDE_PLACEHOLDER6, BuildingID::HORDE_PLACEHOLDER7, BuildingID::HORDE_PLACEHOLDER8 }; //init buildings if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings @@ -823,7 +824,7 @@ void CGameState::initTowns() vti->builtBuildings.insert(BuildingID::VILLAGE_HALL); //init hordes - for (int i = 0; i < GameConstants::CREATURES_PER_TOWN; i++) + for (int i = 0; i < vti->town->creatures.size(); i++) { if (vstd::contains(vti->builtBuildings, hordes[i])) //if we have horde for this level { @@ -863,7 +864,7 @@ void CGameState::initTowns() //town events for(CCastleEvent &ev : vti->events) { - for (int i = 0; igetTown()->creatures.size(); i++) if (vstd::contains(ev.buildings,hordes[i])) //if we have horde for this level { ev.buildings.erase(hordes[i]); @@ -1019,7 +1020,7 @@ const BattleInfo * CGameState::getBattle(const PlayerColor & player) const return nullptr; for (const auto & battlePtr : currentBattles) - if (battlePtr->sides[0].color == player || battlePtr->sides[1].color == player) + if (battlePtr->getSide(BattleSide::ATTACKER).color == player || battlePtr->getSide(BattleSide::DEFENDER).color == player) return battlePtr.get(); return nullptr; diff --git a/lib/gameState/GameStatistics.cpp b/lib/gameState/GameStatistics.cpp index 5c129357c..35721f4a1 100644 --- a/lib/gameState/GameStatistics.cpp +++ b/lib/gameState/GameStatistics.cpp @@ -42,7 +42,7 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons scenarioHighScores.isCampaign = false; data.map = gs->map->name.toString(); - data.timestamp = std::time(0); + data.timestamp = std::time(nullptr); data.day = gs->getDate(Date::DAY); data.player = ps->color; data.playerName = gs->getStartInfo()->playerInfos.at(ps->color).name; @@ -139,7 +139,7 @@ std::string StatisticDataSet::toCsv() ss << entry.playerName << ";"; ss << entry.team.getNum() << ";"; ss << entry.isHuman << ";"; - ss << (int)entry.status << ";"; + ss << static_cast(entry.status) << ";"; ss << entry.numberHeroes << ";"; ss << entry.numberTowns << ";"; ss << entry.numberArtifacts << ";"; @@ -196,8 +196,6 @@ std::vector Statistic::getMines(const CGameState * gs, const Pla { std::vector tmp; - /// FIXME: Dirty dirty hack - /// Stats helper need some access to gamestate. std::vector ownedObjects; for(const CGObjectInstance * obj : gs->map->objects) { @@ -228,7 +226,7 @@ int Statistic::getNumberOfArts(const PlayerState * ps) int ret = 0; for(auto h : ps->heroes) { - ret += (int)h->artifactsInBackpack.size() + (int)h->artifactsWorn.size(); + ret += h->artifactsInBackpack.size() + h->artifactsWorn.size(); } return ret; } @@ -323,9 +321,7 @@ std::vector> Statistic::getRank(std::vector > ret; - std::vector tmp; - tmp.push_back( stats[0].first ); - ret.push_back( tmp ); + ret.push_back( { stats[0].first } ); //the rest of elements for(int g=1; g> Statistic::getRank(std::vector tmp; - tmp.push_back(stats[g].first); - ret.push_back(tmp); + ret.push_back( { stats[g].first }); } } @@ -358,7 +352,7 @@ float Statistic::getObeliskVisitedRatio(const CGameState * gs, const TeamID & t) { if(!gs->map->obeliskCount) return 0; - return (float)getObeliskVisited(gs, t) / (float)gs->map->obeliskCount; + return static_cast(getObeliskVisited(gs, t)) / gs->map->obeliskCount; } std::map Statistic::getNumMines(const CGameState * gs, const PlayerState * ps) diff --git a/lib/gameState/HighScore.cpp b/lib/gameState/HighScore.cpp index 51759629e..a43fe7978 100644 --- a/lib/gameState/HighScore.cpp +++ b/lib/gameState/HighScore.cpp @@ -52,8 +52,8 @@ HighScoreCalculation::Result HighScoreCalculation::calculate() { Result firstResult; Result summary; - const std::array difficultyMultipliers{0.8, 1.0, 1.3, 1.6, 2.0}; - for(auto & param : parameters) + const std::array difficultyMultipliers{0.8, 1.0, 1.3, 1.6, 2.0}; + for(const auto & param : parameters) { double tmp = 200 - (param.day + 10) / (param.townAmount + 5) + (param.allEnemiesDefeated ? 25 : 0) + (param.hasGrail ? 25 : 0); firstResult = Result{static_cast(tmp), static_cast(tmp * difficultyMultipliers.at(param.difficulty)), param.day, param.usedCheat}; diff --git a/lib/logging/VisualLogger.cpp b/lib/logging/VisualLogger.cpp index 3cb6130e2..2decb74d2 100644 --- a/lib/logging/VisualLogger.cpp +++ b/lib/logging/VisualLogger.cpp @@ -15,9 +15,9 @@ VCMI_LIB_NAMESPACE_BEGIN DLL_LINKAGE VisualLogger * logVisual = new VisualLogger(); -void VisualLogger::updateWithLock(std::string channel, std::function func) +void VisualLogger::updateWithLock(const std::string & channel, const std::function & func) { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); mapLines[channel].clear(); mapTexts[channel].clear(); @@ -30,21 +30,21 @@ void VisualLogger::updateWithLock(std::string channel, std::function lock(mutex); + std::lock_guard lock(mutex); - for(auto line : mapLines[keyToShow]) + for(const auto & line : mapLines[keyToShow]) { visulizer.drawLine(line.start, line.end); } std::map>> textMap; - for(auto line : mapTexts[keyToShow]) + for(const auto & line : mapTexts[keyToShow]) { textMap[line.tile].push_back(line); } - for(auto & pair : textMap) + for(const auto & pair : textMap) { for(int i = 0; i < pair.second.size(); i++) { @@ -55,7 +55,7 @@ void VisualLogger::visualize(IMapOverlayLogVisualizer & visulizer) void VisualLogger::visualize(IBattleOverlayLogVisualizer & visulizer) { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); std::map> textMap; for(auto line : battleTexts[keyToShow]) @@ -72,12 +72,12 @@ void VisualLogger::visualize(IBattleOverlayLogVisualizer & visulizer) } } -void VisualLogger::setKey(std::string key) +void VisualLogger::setKey(const std::string & key) { keyToShow = key; } -void IVisualLogBuilder::addText(int3 tile, std::string text, PlayerColor background) +void IVisualLogBuilder::addText(int3 tile, const std::string & text, PlayerColor background) { std::optional rgbColor; diff --git a/lib/logging/VisualLogger.h b/lib/logging/VisualLogger.h index 6f45a8a86..6fe0485dd 100644 --- a/lib/logging/VisualLogger.h +++ b/lib/logging/VisualLogger.h @@ -20,23 +20,23 @@ class IMapOverlayLogVisualizer { public: virtual void drawLine(int3 start, int3 end) = 0; - virtual void drawText(int3 tile, int lineNumber, std::string text, std::optional background) = 0; + virtual void drawText(int3 tile, int lineNumber, const std::string & text, const std::optional & color) = 0; }; class IBattleOverlayLogVisualizer { public: - virtual void drawText(BattleHex tile, int lineNumber, std::string text) = 0; + virtual void drawText(BattleHex tile, int lineNumber, const std::string & text) = 0; }; class DLL_LINKAGE IVisualLogBuilder { public: virtual void addLine(int3 start, int3 end) = 0; - virtual void addText(int3 tile, std::string text, std::optional color = {}) = 0; - virtual void addText(BattleHex tile, std::string text) = 0; + virtual void addText(int3 tile, const std::string & text, const std::optional & color = {}) = 0; + virtual void addText(BattleHex tile, const std::string & text) = 0; - void addText(int3 tile, std::string text, PlayerColor background); + void addText(int3 tile, const std::string & text, PlayerColor background); }; /// The logger is used to show screen overlay @@ -89,12 +89,12 @@ private: mapLines.emplace_back(start, end); } - void addText(BattleHex tile, std::string text) override + void addText(BattleHex tile, const std::string & text) override { battleTexts.emplace_back(tile, text, std::optional()); } - void addText(int3 tile, std::string text, std::optional background) override + void addText(int3 tile, const std::string & text, const std::optional & background) override { mapTexts.emplace_back(tile, text, background); } @@ -109,10 +109,10 @@ private: public: - void updateWithLock(std::string channel, std::function func); + void updateWithLock(const std::string & channel, const std::function & func); void visualize(IMapOverlayLogVisualizer & visulizer); void visualize(IBattleOverlayLogVisualizer & visulizer); - void setKey(std::string key); + void setKey(const std::string & key); }; extern DLL_LINKAGE VisualLogger * logVisual; diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index bd5d1af68..43737f887 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -391,7 +391,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if (result.winner == 0) + if (result.winner == BattleSide::ATTACKER) { doVisit(hero); } diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index efba48004..389ce862b 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -480,12 +480,12 @@ void CGCreature::flee( const CGHeroInstance * h ) const void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner == 0) + if(result.winner == BattleSide::ATTACKER) { giveReward(hero); cb->removeObject(this, hero->getOwner()); } - else if(result.winner > 1) // draw + else if(result.winner == BattleSide::NONE) // draw { // guarded reward is lost forever on draw cb->removeObject(this, hero->getOwner()); diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 37a717960..c810f1307 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -510,7 +510,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const void CGDwelling::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if (result.winner == 0) + if (result.winner == BattleSide::ATTACKER) { onHeroVisit(hero); } diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index a4941ea37..f14e92fce 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -31,6 +31,7 @@ #include "../StartInfo.h" #include "CGTownInstance.h" #include "../entities/faction/CTownHandler.h" +#include "../battle/CBattleInfoEssentials.h" #include "../campaign/CampaignState.h" #include "../json/JsonBonus.h" #include "../pathfinder/TurnInfo.h" @@ -880,7 +881,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b double necromancySkill = valOfBonuses(BonusType::UNDEAD_RAISE_PERCENTAGE) / 100.0; const ui8 necromancyLevel = valOfBonuses(BonusType::IMPROVED_NECROMANCY); vstd::amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all... - const std::map &casualties = battleResult.casualties[!battleResult.winner]; + const std::map &casualties = battleResult.casualties[CBattleInfoEssentials::otherSide(battleResult.winner)]; // figure out what to raise - pick strongest creature meeting requirements CreatureID creatureTypeRaised = CreatureID::NONE; //now we always have IMPROVED_NECROMANCY, no need for hardcode int requiredCasualtyLevel = 1; diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index c9ede1335..8e931cf90 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -180,7 +180,7 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner == 0) + if(result.winner == BattleSide::ATTACKER) { CRewardableObject::onHeroVisit(hero); } diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 04feb9d09..f82849523 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -128,7 +128,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const { GrowthInfo ret; - if (level<0 || level >=GameConstants::CREATURES_PER_TOWN) + if (level<0 || level >=town->creatures.size()) return ret; if (creatures[level].second.empty()) return ret; //no dwelling @@ -514,16 +514,16 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures blockVisit = true; if(townEnvisagesBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example - creatures.resize(GameConstants::CREATURES_PER_TOWN + 1); + creatures.resize(town->creatures.size() + 1); else - creatures.resize(GameConstants::CREATURES_PER_TOWN); + creatures.resize(town->creatures.size()); - for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++) + for (int level = 0; level < town->creatures.size(); level++) { - BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST + level); + BuildingID buildID = BuildingID(BuildingID::getDwellingFromLevel(level, 0)); int upgradeNum = 0; - for (; town->buildings.count(buildID); upgradeNum++, buildID.advance(GameConstants::CREATURES_PER_TOWN)) + for (; town->buildings.count(buildID); upgradeNum++, buildID.advance(town->creatures.size())) { if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum) creatures[level].second.push_back(town->creatures[level][upgradeNum]); @@ -591,7 +591,7 @@ void CGTownInstance::newTurn(vstd::RNG & rand) const } if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack { - int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1); + int i = rand.nextInt(std::min((int)town->creatures.size(), cb->getDate(Date::MONTH) << 1) - 1); if (!town->creatures[i].empty()) { CreatureID c = town->creatures[i][0]; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 95059c339..8c01200c3 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -205,7 +205,7 @@ ui32 CGMine::getProducedQuantity() const void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner == 0) //attacker won + if(result.winner == BattleSide::ATTACKER) //attacker won { if(isAbandoned()) { @@ -344,7 +344,7 @@ void CGResource::collectRes(const PlayerColor & player) const void CGResource::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner == 0) //attacker won + if(result.winner == BattleSide::ATTACKER) //attacker won collectRes(hero->getOwner()); } @@ -911,7 +911,7 @@ BattleField CGArtifact::getBattlefield() const void CGArtifact::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner == 0) //attacker won + if(result.winner == BattleSide::ATTACKER) //attacker won pick(hero); } @@ -1010,7 +1010,7 @@ bool CGGarrison::passableFor(PlayerColor player) const void CGGarrison::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if (result.winner == 0) + if (result.winner == BattleSide::ATTACKER) onHeroVisit(hero); } diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 96d07918d..f4ad85b46 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -44,8 +44,7 @@ DisposedHero::DisposedHero() : heroId(0), portrait(255) } CMapEvent::CMapEvent() - : players(0) - , humanAffected(false) + : humanAffected(false) , computerAffected(false) , firstOccurrence(0) , nextOccurrence(0) @@ -53,21 +52,51 @@ CMapEvent::CMapEvent() } -bool CMapEvent::earlierThan(const CMapEvent & other) const +bool CMapEvent::occursToday(int currentDay) const { - return firstOccurrence < other.firstOccurrence; + if (currentDay == firstOccurrence + 1) + return true; + + if (nextOccurrence == 0) + return false; + + if (currentDay < firstOccurrence) + return false; + + return (currentDay - firstOccurrence - 1) % nextOccurrence == 0; } -bool CMapEvent::earlierThanOrEqual(const CMapEvent & other) const +bool CMapEvent::affectsPlayer(PlayerColor color, bool isHuman) const { - return firstOccurrence <= other.firstOccurrence; + if (players.count(color) == 0) + return false; + + if (!isHuman && !computerAffected) + return false; + + if (isHuman && !humanAffected) + return false; + + return true; } void CMapEvent::serializeJson(JsonSerializeFormat & handler) { handler.serializeString("name", name); handler.serializeStruct("message", message); - handler.serializeInt("players", players); + if (!handler.saving && handler.getCurrent()["players"].isNumber()) + { + // compatibility for old maps + int playersMask = 0; + handler.serializeInt("players", playersMask); + for (int i = 0; i < 8; ++i) + if ((playersMask & (1 << i)) != 0) + players.insert(PlayerColor(i)); + } + else + { + handler.serializeIdArray("players", players); + } handler.serializeInt("humanAffected", humanAffected); handler.serializeInt("computerAffected", computerAffected); handler.serializeInt("firstOccurrence", firstOccurrence); diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 772520192..0fbee42c3 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -136,7 +136,7 @@ public: std::set allowedSpells; std::set allowedArtifact; std::set allowedAbilities; - std::list events; + std::vector events; int3 grailPos; int grailRadius; diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h index aa3e767a9..36034db33 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/CMapDefines.h @@ -30,13 +30,13 @@ public: CMapEvent(); virtual ~CMapEvent() = default; - bool earlierThan(const CMapEvent & other) const; - bool earlierThanOrEqual(const CMapEvent & other) const; + bool occursToday(int currentDay) const; + bool affectsPlayer(PlayerColor player, bool isHuman) const; std::string name; MetaString message; TResources resources; - ui8 players; // affected players, bit field? + std::set players; bool humanAffected; bool computerAffected; ui32 firstOccurrence; @@ -48,7 +48,18 @@ public: h & name; h & message; h & resources; - h & players; + if (h.version >= Handler::Version::EVENTS_PLAYER_SET) + { + h & players; + } + else + { + ui8 playersMask = 0; + h & playersMask; + for (int i = 0; i < 8; ++i) + if ((playersMask & (1 << i)) != 0) + players.insert(PlayerColor(i)); + } h & humanAffected; h & computerAffected; h & firstOccurrence; diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index 2884dbb12..59d897a74 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -124,26 +124,26 @@ CMap * CMapEditManager::getMap() return map; } -void CMapEditManager::clearTerrain(vstd::RNG * gen) +void CMapEditManager::clearTerrain(vstd::RNG * customGen) { - execute(std::make_unique(map, gen ? gen : this->gen.get())); + execute(std::make_unique(map, customGen ? customGen : gen.get())); } -void CMapEditManager::drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * gen) +void CMapEditManager::drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * customGen) { - execute(std::make_unique(map, terrainSel, terType, decorationsPercentage, gen ? gen : this->gen.get())); + execute(std::make_unique(map, terrainSel, terType, decorationsPercentage, customGen ? customGen : gen.get())); terrainSel.clearSelection(); } -void CMapEditManager::drawRoad(RoadId roadType, vstd::RNG* gen) +void CMapEditManager::drawRoad(RoadId roadType, vstd::RNG* customGen) { - execute(std::make_unique(map, terrainSel, roadType, gen ? gen : this->gen.get())); + execute(std::make_unique(map, terrainSel, roadType, customGen ? customGen : gen.get())); terrainSel.clearSelection(); } -void CMapEditManager::drawRiver(RiverId riverType, vstd::RNG* gen) +void CMapEditManager::drawRiver(RiverId riverType, vstd::RNG* customGen) { - execute(std::make_unique(map, terrainSel, riverType, gen ? gen : this->gen.get())); + execute(std::make_unique(map, terrainSel, riverType, customGen ? customGen : gen.get())); terrainSel.clearSelection(); } diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 2565084f6..18b51e1b6 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -2244,7 +2244,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt reader->readResources(event.resources); - event.players = reader->readUInt8(); + reader->readBitmaskPlayers(event.players, false); if(features.levelSOD) event.humanAffected = reader->readBool(); else @@ -2313,7 +2313,7 @@ void CMapLoaderH3M::readEvents() event.message.appendTextID(readLocalizedString(TextIdentifier("event", eventID, "description"))); reader->readResources(event.resources); - event.players = reader->readUInt8(); + reader->readBitmaskPlayers(event.players, false); if(features.levelSOD) { event.humanAffected = reader->readBool(); diff --git a/lib/network/NetworkConnection.cpp b/lib/network/NetworkConnection.cpp index d77a1eb8c..73e1ed47b 100644 --- a/lib/network/NetworkConnection.cpp +++ b/lib/network/NetworkConnection.cpp @@ -136,7 +136,7 @@ void NetworkConnection::setAsyncWritesEnabled(bool on) void NetworkConnection::sendPacket(const std::vector & message) { - std::lock_guard lock(writeMutex); + std::lock_guard lock(writeMutex); std::vector headerVector(sizeof(uint32_t)); uint32_t messageSize = message.size(); std::memcpy(headerVector.data(), &messageSize, sizeof(uint32_t)); @@ -177,7 +177,7 @@ void NetworkConnection::doSendData() void NetworkConnection::onDataSent(const boost::system::error_code & ec) { - std::lock_guard lock(writeMutex); + std::lock_guard lock(writeMutex); dataToSend.pop_front(); if (ec) { diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index faeaecf1b..488367624 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -55,8 +55,6 @@ public: virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {} virtual void visitAddQuest(AddQuest & pack) {} virtual void visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack) {} - virtual void visitUpdateMapEvents(UpdateMapEvents & pack) {} - virtual void visitUpdateCastleEvents(UpdateCastleEvents & pack) {} virtual void visitChangeFormation(ChangeFormation & pack) {} virtual void visitRemoveObject(RemoveObject & pack) {} virtual void visitTryMoveHero(TryMoveHero & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 8fec89864..c242b23b9 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -225,16 +225,6 @@ void UpdateArtHandlerLists::visitTyped(ICPackVisitor & visitor) visitor.visitUpdateArtHandlerLists(*this); } -void UpdateMapEvents::visitTyped(ICPackVisitor & visitor) -{ - visitor.visitUpdateMapEvents(*this); -} - -void UpdateCastleEvents::visitTyped(ICPackVisitor & visitor) -{ - visitor.visitUpdateCastleEvents(*this); -} - void ChangeFormation::visitTyped(ICPackVisitor & visitor) { visitor.visitChangeFormation(*this); @@ -907,17 +897,6 @@ void UpdateArtHandlerLists::applyGs(CGameState * gs) const gs->allocatedArtifacts = allocatedArtifacts; } -void UpdateMapEvents::applyGs(CGameState * gs) const -{ - gs->map->events = events; -} - -void UpdateCastleEvents::applyGs(CGameState * gs) const -{ - auto * t = gs->getTown(town); - t->events = events; -} - void ChangeFormation::applyGs(CGameState * gs) const { gs->getHero(hid)->setFormation(formation); @@ -1496,7 +1475,7 @@ void NewObject::applyGs(CGameState *gs) gs->map->addBlockVisTiles(newObject); gs->map->calculateGuardingGreaturePositions(); - logGlobal->debug("Added object id=%d; address=%x; name=%s", newObject->id, (intptr_t)newObject, newObject->getObjectName()); + logGlobal->debug("Added object id=%d; name=%s", newObject->id, newObject->getObjectName()); } void NewArtifact::applyGs(CGameState *gs) @@ -2114,7 +2093,7 @@ void BattleResultAccepted::applyGs(CGameState * gs) const res.hero->removeBonusesRecursive(Bonus::OneBattle); } - if(winnerSide != 2) + if(winnerSide != BattleSide::NONE) { // Grow up growing artifacts const auto hero = heroResult[winnerSide].hero; @@ -2135,10 +2114,10 @@ void BattleResultAccepted::applyGs(CGameState * gs) const if(VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) { - if(heroResult[0].army) - heroResult[0].army->giveStackExp(heroResult[0].exp); - if(heroResult[1].army) - heroResult[1].army->giveStackExp(heroResult[1].exp); + if(heroResult[BattleSide::ATTACKER].army) + heroResult[BattleSide::ATTACKER].army->giveStackExp(heroResult[BattleSide::ATTACKER].exp); + if(heroResult[BattleSide::DEFENDER].army) + heroResult[BattleSide::DEFENDER].army->giveStackExp(heroResult[BattleSide::DEFENDER].exp); CBonusSystemNode::treeHasChanged(); } @@ -2242,19 +2221,14 @@ void StartAction::applyGs(CGameState *gs) else { if(ba.actionType == EActionType::HERO_SPELL) - gs->getBattle(battleID)->sides[ba.side].usedSpellsHistory.push_back(ba.spell); + gs->getBattle(battleID)->getSide(ba.side).usedSpellsHistory.push_back(ba.spell); } } void BattleSpellCast::applyGs(CGameState * gs) const { - if(castByHero) - { - if(side < 2) - { - gs->getBattle(battleID)->sides[side].castSpellsCount++; - } - } + if(castByHero && side != BattleSide::NONE) + gs->getBattle(battleID)->getSide(side).castSpellsCount++; } void SetStackEffect::applyGs(CGameState *gs) @@ -2388,7 +2362,7 @@ void BattleSetStackProperty::applyGs(CGameState * gs) const } case ENCHANTER_COUNTER: { - auto & counter = gs->getBattle(battleID)->sides[gs->getBattle(battleID)->whatSide(stack->unitOwner())].enchanterCounter; + auto & counter = gs->getBattle(battleID)->getSide(gs->getBattle(battleID)->whatSide(stack->unitOwner())).enchanterCounter; if(absolute) counter = val; else diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 678087485..86601b3b8 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -549,34 +549,6 @@ struct DLL_LINKAGE UpdateArtHandlerLists : public CPackForClient } }; -struct DLL_LINKAGE UpdateMapEvents : public CPackForClient -{ - std::list events; - - void applyGs(CGameState * gs) const; - void visitTyped(ICPackVisitor & visitor) override; - - template void serialize(Handler & h) - { - h & events; - } -}; - -struct DLL_LINKAGE UpdateCastleEvents : public CPackForClient -{ - ObjectInstanceID town; - std::vector events; - - void applyGs(CGameState * gs) const; - void visitTyped(ICPackVisitor & visitor) override; - - template void serialize(Handler & h) - { - h & town; - h & events; - } -}; - struct DLL_LINKAGE ChangeFormation : public CPackForClient { ObjectInstanceID hid; diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index b9d498794..db3aaa3ed 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -109,8 +109,8 @@ struct DLL_LINKAGE BattleResultAccepted : public CPackForClient }; BattleID battleID = BattleID::NONE; - std::array heroResult; - ui8 winnerSide; + BattleSideArray heroResult; + BattleSide winnerSide; template void serialize(Handler & h) { @@ -127,9 +127,9 @@ struct DLL_LINKAGE BattleResult : public Query BattleID battleID = BattleID::NONE; EBattleResult result = EBattleResult::NORMAL; - ui8 winner = 2; //0 - attacker, 1 - defender, [2 - draw (should be possible?)] - std::map casualties[2]; //first => casualties of attackers - map crid => number - TExpType exp[2] = {0, 0}; //exp for attacker and defender + BattleSide winner = BattleSide::NONE; //0 - attacker, 1 - defender, [2 - draw (should be possible?)] + BattleSideArray> casualties; //first => casualties of attackers - map crid => number + BattleSideArray exp{0,0}; //exp for attacker and defender std::set artifacts; //artifacts taken from loser to winner - currently unused void visitTyped(ICPackVisitor & visitor) override; @@ -140,8 +140,7 @@ struct DLL_LINKAGE BattleResult : public Query h & queryID; h & result; h & winner; - h & casualties[0]; - h & casualties[1]; + h & casualties; h & exp; h & artifacts; assert(battleID != BattleID::NONE); @@ -369,7 +368,7 @@ struct DLL_LINKAGE BattleSpellCast : public CPackForClient BattleID battleID = BattleID::NONE; bool activeCast = true; - ui8 side = 0; //which hero did cast spell: 0 - attacker, 1 - defender + BattleSide side = BattleSide::NONE; //which hero did cast spell SpellID spellID; //id of spell ui8 manaGained = 0; //mana channeling ability BattleHex tile; //destination tile (may not be set in some global/mass spells diff --git a/lib/registerTypes/RegisterTypesClientPacks.h b/lib/registerTypes/RegisterTypesClientPacks.h index af64194ab..d81593818 100644 --- a/lib/registerTypes/RegisterTypesClientPacks.h +++ b/lib/registerTypes/RegisterTypesClientPacks.h @@ -44,8 +44,6 @@ void registerTypesClientPacks(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); - s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 9e966f40c..ba355b3cd 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -58,7 +58,7 @@ class BinaryDeserializer : public CLoaderBase if(reader->smartVectorMembersSerialization) { if(const auto *info = reader->getVectorizedTypeInfo()) - armyPtr = static_cast(reader->getVectorItemFromId(*info, armyID)); + armyPtr = reader->getVectorItemFromId(*info, armyID); } if(slot != SlotID::COMMANDER_SLOT_PLACEHOLDER) diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index a243b4373..30a45da0e 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -50,6 +50,7 @@ enum class ESerializationVersion : int32_t BANK_UNIT_PLACEMENT, // 843 Banks have unit placement flag RELEASE_152 = BANK_UNIT_PLACEMENT, + RELEASE_156 = BANK_UNIT_PLACEMENT, COMPACT_STRING_SERIALIZATION, // 844 - optimized serialization of previously encountered strings COMPACT_INTEGER_SERIALIZATION, // 845 - serialize integers in forms similar to protobuf @@ -58,8 +59,10 @@ enum class ESerializationVersion : int32_t MAP_FORMAT_ADDITIONAL_INFOS, // 848 - serialize new infos in map format REMOVE_LIB_RNG, // 849 - removed random number generators from library classes HIGHSCORE_PARAMETERS, // 850 - saves parameter for campaign - PLAYER_HANDICAP, // 851 - player handicap selection at game start + PLAYER_HANDICAP, // 851 - player handicap selection at game start STATISTICS, // 852 - removed random number generators from library classes + CAMPAIGN_REGIONS, // 853 - configurable campaign regions + EVENTS_PLAYER_SET, // 854 - map & town events use std::set instead of bitmask to store player list - CURRENT = STATISTICS + CURRENT = EVENTS_PLAYER_SET }; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index c9d6b70b1..62954d66b 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -193,16 +193,16 @@ bool BattleSpellMechanics::canBeCast(Problem & problem) const return adaptProblem(ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL, problem); const PlayerColor player = caster->getCasterOwner(); - const auto side = battle()->playerToSide(player); + const BattleSide side = battle()->playerToSide(player); - if(!side) + if(side == BattleSide::NONE) return adaptProblem(ESpellCastProblem::INVALID, problem); //effect like Recanter's Cloak. Blocks also passive casting. //TODO: check creature abilities to block //TODO: check any possible caster - if(battle()->battleMaxSpellLevel(side.value()) < getSpellLevel() || battle()->battleMinSpellLevel(side.value()) > getSpellLevel()) + if(battle()->battleMaxSpellLevel(side) < getSpellLevel() || battle()->battleMinSpellLevel(side) > getSpellLevel()) return adaptProblem(ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED, problem); return effects->applicable(problem, this); @@ -284,7 +284,7 @@ void BattleSpellMechanics::cast(ServerCallback * server, const Target & target) const CGHeroInstance * otherHero = nullptr; { //check it there is opponent hero - const ui8 otherSide = battle()->otherSide(casterSide); + const BattleSide otherSide = battle()->otherSide(casterSide); if(battle()->battleHasHero(otherSide)) otherHero = battle()->battleGetFightingHero(otherSide); @@ -505,62 +505,14 @@ std::set BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex using namespace SRSLPraserHelpers; std::set ret; - std::string rng = owner->getLevelInfo(getRangeLevel()).range + ','; //copy + artificial comma for easier handling + std::vector rng = owner->getLevelInfo(getRangeLevel()).range; - if(rng.size() >= 2 && rng[0] != 'X') //there is at least one hex in range (+artificial comma) + for(auto & elem : rng) { - std::string number1; - std::string number2; - int beg = 0; - int end = 0; - bool readingFirst = true; - for(auto & elem : rng) - { - if(std::isdigit(elem) ) //reading number - { - if(readingFirst) - number1 += elem; - else - number2 += elem; - } - else if(elem == ',') //comma - { - //calculating variables - if(readingFirst) - { - beg = std::stoi(number1); - number1 = ""; - } - else - { - end = std::stoi(number2); - number2 = ""; - } - //obtaining new hexes - std::set curLayer; - if(readingFirst) - { - curLayer = getInRange(centralHex, beg, beg); - } - else - { - curLayer = getInRange(centralHex, beg, end); - readingFirst = true; - } - //adding obtained hexes - for(const auto & curLayer_it : curLayer) - { - ret.insert(curLayer_it); - } - - } - else if(elem == '-') //dash - { - beg = std::stoi(number1); - number1 = ""; - readingFirst = false; - } - } + std::set curLayer = getInRange(centralHex, elem, elem); + //adding obtained hexes + for(const auto & curLayer_it : curLayer) + ret.insert(curLayer_it); } return ret; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 556663864..037ff4a4f 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -562,7 +562,7 @@ CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, spells::Mo const auto & levelInfo = spell->getLevelInfo(level); smart = levelInfo.smartTarget; - massive = levelInfo.range == "X"; + massive = levelInfo.range.empty(); clearAffected = levelInfo.clearAffected; clearTarget = levelInfo.clearTarget; } @@ -682,6 +682,64 @@ const std::vector & CSpellHandler::getTypeNames() const return typeNames; } +std::vector CSpellHandler::spellRangeInHexes(std::string input) const +{ + std::set ret; + std::string rng = input + ','; //copy + artificial comma for easier handling + + if(rng.size() >= 2 && rng[0] != 'X') //there is at least one hex in range (+artificial comma) + { + std::string number1; + std::string number2; + int beg = 0; + int end = 0; + bool readingFirst = true; + for(auto & elem : rng) + { + if(std::isdigit(elem) ) //reading number + { + if(readingFirst) + number1 += elem; + else + number2 += elem; + } + else if(elem == ',') //comma + { + //calculating variables + if(readingFirst) + { + beg = std::stoi(number1); + number1 = ""; + } + else + { + end = std::stoi(number2); + number2 = ""; + } + //obtaining new hexes + std::set curLayer; + if(readingFirst) + { + ret.insert(beg); + } + else + { + for(int i = beg; i <= end; ++i) + ret.insert(i); + } + } + else if(elem == '-') //dash + { + beg = std::stoi(number1); + number1 = ""; + readingFirst = false; + } + } + } + + return std::vector(ret.begin(), ret.end()); +} + std::shared_ptr CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) { assert(identifier.find(':') == std::string::npos); @@ -936,7 +994,7 @@ std::shared_ptr CSpellHandler::loadFromJson(const std::string & scope, c levelObject.smartTarget = levelNode["targetModifier"]["smart"].Bool(); levelObject.clearTarget = levelNode["targetModifier"]["clearTarget"].Bool(); levelObject.clearAffected = levelNode["targetModifier"]["clearAffected"].Bool(); - levelObject.range = levelNode["range"].String(); + levelObject.range = spellRangeInHexes(levelNode["range"].String()); for(const auto & elem : levelNode["effects"].Struct()) { diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index b372bcbbd..32b91f3fb 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -67,12 +67,6 @@ public: ///resource name AnimationPath resourceName; - - template void serialize(Handler & h) - { - h & minimumAngle; - h & resourceName; - } }; struct AnimationItem @@ -83,14 +77,6 @@ public: int pause; AnimationItem(); - - template void serialize(Handler & h) - { - h & resourceName; - h & effectName; - h & verticalPosition; - h & pause; - } }; using TAnimation = AnimationItem; @@ -111,14 +97,6 @@ public: ///use selectProjectile to access std::vector projectile; - template void serialize(Handler & h) - { - h & projectile; - h & hit; - h & cast; - h & affect; - } - AnimationPath selectProjectile(const double angle) const; } animationInfo; @@ -132,27 +110,13 @@ public: bool smartTarget = true; bool clearTarget = false; bool clearAffected = false; - std::string range = "0"; + std::vector range = { 0 }; //TODO: remove these two when AI will understand special effects std::vector> effects; //deprecated std::vector> cumulativeEffects; //deprecated JsonNode battleEffects; - - template void serialize(Handler & h) - { - h & cost; - h & power; - h & AIValue; - h & smartTarget; - h & range; - h & effects; - h & cumulativeEffects; - h & clearTarget; - h & clearAffected; - h & battleEffects; - } }; /** \brief Low level accessor. Don`t use it if absolutely necessary @@ -342,6 +306,8 @@ bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spe class DLL_LINKAGE CSpellHandler: public CHandlerBase { + std::vector spellRangeInHexes(std::string rng) const; + public: ///IHandler base std::vector loadLegacyData() override; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 8a1593a73..a1488e1e8 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -397,7 +397,7 @@ std::unique_ptr ISpellMechanicsFactory::get(const CSpell ///Mechanics Mechanics::Mechanics() : caster(nullptr), - casterSide(0) + casterSide(BattleSide::NONE) { } @@ -413,8 +413,7 @@ BaseMechanics::BaseMechanics(const IBattleCast * event): { caster = event->getCaster(); - //FIXME: do not crash on invalid side - casterSide = cb->playerToSide(caster->getCasterOwner()).value(); + casterSide = cb->playerToSide(caster->getCasterOwner()); { auto value = event->getSpellLevel(); diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index ea150497d..35fedbf72 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -252,7 +252,7 @@ public: const Caster * caster; - ui8 casterSide; + BattleSide casterSide; protected: Mechanics(); diff --git a/lib/spells/effects/Moat.cpp b/lib/spells/effects/Moat.cpp index ddcef5f5a..653e146e9 100644 --- a/lib/spells/effects/Moat.cpp +++ b/lib/spells/effects/Moat.cpp @@ -134,7 +134,7 @@ void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const Ef BattleObstaclesChanged pack; pack.battleID = m->battle()->getBattle()->getBattleID(); - auto all = m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING); + auto all = m->battle()->battleGetAllObstacles(BattleSide::ALL_KNOWING); int obstacleIdToGive = 1; for(auto & one : all) diff --git a/lib/spells/effects/Obstacle.cpp b/lib/spells/effects/Obstacle.cpp index e75052684..8359e3e0d 100644 --- a/lib/spells/effects/Obstacle.cpp +++ b/lib/spells/effects/Obstacle.cpp @@ -274,7 +274,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons BattleObstaclesChanged pack; pack.battleID = m->battle()->getBattle()->getBattleID(); - auto all = m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING); + auto all = m->battle()->battleGetAllObstacles(BattleSide::ALL_KNOWING); int obstacleIdToGive = 1; for(auto & one : all) diff --git a/lib/spells/effects/Obstacle.h b/lib/spells/effects/Obstacle.h index 1dd256e1a..b6410aed4 100644 --- a/lib/spells/effects/Obstacle.h +++ b/lib/spells/effects/Obstacle.h @@ -66,7 +66,7 @@ private: bool passable = false; int32_t turnsRemaining = -1; - std::array sideOptions; + BattleSideArray sideOptions; static bool isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); static bool noRoomToPlace(Problem & problem, const Mechanics * m); diff --git a/lib/spells/effects/RemoveObstacle.cpp b/lib/spells/effects/RemoveObstacle.cpp index f24b0e881..58e462c69 100644 --- a/lib/spells/effects/RemoveObstacle.cpp +++ b/lib/spells/effects/RemoveObstacle.cpp @@ -90,7 +90,7 @@ std::set RemoveObstacle::getTargets(const Mechanics * std::set possibleTargets; if(m->isMassive() || alwaysMassive) { - for(const auto & obstacle : m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING)) + for(const auto & obstacle : m->battle()->battleGetAllObstacles(BattleSide::ALL_KNOWING)) if(canRemove(obstacle.get())) possibleTargets.insert(obstacle.get()); } diff --git a/lib/texts/CGeneralTextHandler.h b/lib/texts/CGeneralTextHandler.h index 0513085de..ce5263ced 100644 --- a/lib/texts/CGeneralTextHandler.h +++ b/lib/texts/CGeneralTextHandler.h @@ -58,7 +58,9 @@ public: LegacyTextContainer turnDurations; //turn durations for pregame (1 Minute ... Unlimited) //towns - LegacyTextContainer tcommands, hcommands, fcommands; //texts for town screen, town hall screen and fort screen + LegacyTextContainer tcommands; //texts for town screen, + LegacyTextContainer hcommands; // town hall screen + LegacyTextContainer fcommands; // fort screen LegacyTextContainer tavernInfo; LegacyTextContainer tavernRumors; diff --git a/lib/texts/CLegacyConfigParser.cpp b/lib/texts/CLegacyConfigParser.cpp index 753c8c863..15ea93c74 100644 --- a/lib/texts/CLegacyConfigParser.cpp +++ b/lib/texts/CLegacyConfigParser.cpp @@ -48,7 +48,7 @@ std::string CLegacyConfigParser::extractQuotedPart() assert(*curr == '\"'); curr++; // skip quote - char * begin = curr; + const char * begin = curr; while (curr != end && *curr != '\"' && *curr != '\t') curr++; @@ -73,7 +73,7 @@ std::string CLegacyConfigParser::extractQuotedString() //extract normal part else if(curr < end && *curr != '\t' && *curr != '\r') { - char * begin = curr; + const char * begin = curr; while (curr < end && *curr != '\t' && *curr != '\r' && *curr != '\"')//find end of string or next quoted part start curr++; @@ -90,7 +90,7 @@ std::string CLegacyConfigParser::extractQuotedString() std::string CLegacyConfigParser::extractNormalString() { - char * begin = curr; + const char * begin = curr; while (curr < end && *curr != '\t' && *curr != '\r')//find end of string curr++; @@ -140,7 +140,7 @@ float CLegacyConfigParser::readNumber() bool CLegacyConfigParser::isNextEntryEmpty() const { - char * nextSymbol = curr; + const char * nextSymbol = curr; while (nextSymbol < end && *nextSymbol == ' ') nextSymbol++; //find next meaningful symbol diff --git a/lib/texts/CLegacyConfigParser.h b/lib/texts/CLegacyConfigParser.h index 7918590e7..41665e0d5 100644 --- a/lib/texts/CLegacyConfigParser.h +++ b/lib/texts/CLegacyConfigParser.h @@ -13,18 +13,14 @@ VCMI_LIB_NAMESPACE_BEGIN -//class CInputStream; -//class JsonNode; -//class JsonSerializeFormat; - /// Parser for any text files from H3 class DLL_LINKAGE CLegacyConfigParser { std::string fileEncoding; std::unique_ptr data; - char * curr; - char * end; + const char * curr; + const char * end; /// extracts part of quoted string. std::string extractQuotedPart(); @@ -49,7 +45,7 @@ public: std::vector ret; ret.reserve(size); while (size--) - ret.push_back((numeric)readNumber()); + ret.push_back(readNumber()); return ret; } diff --git a/lib/texts/TextLocalizationContainer.h b/lib/texts/TextLocalizationContainer.h index 0e9e3f05f..28aabd640 100644 --- a/lib/texts/TextLocalizationContainer.h +++ b/lib/texts/TextLocalizationContainer.h @@ -99,7 +99,7 @@ public: template void serialize(Handler & h) { - std::lock_guard globalLock(globalTextMutex); + std::lock_guard globalLock(globalTextMutex); if (h.version >= Handler::Version::SIMPLE_TEXT_CONTAINER_SERIALIZATION) { @@ -123,7 +123,7 @@ public: if(h.saving) { - for(auto s : stringsLocalizations) + for(auto & s : stringsLocalizations) { key = s.first; h & key; diff --git a/mapeditor/inspector/townbuildingswidget.cpp b/mapeditor/inspector/townbuildingswidget.cpp index 775c735f9..c3bff0216 100644 --- a/mapeditor/inspector/townbuildingswidget.cpp +++ b/mapeditor/inspector/townbuildingswidget.cpp @@ -57,6 +57,7 @@ std::string defaultBuildingIdConversion(BuildingID bId) case BuildingID::DWELL_LVL_5: return "DWELL_LVL_5"; case BuildingID::DWELL_LVL_6: return "DWELL_LVL_6"; case BuildingID::DWELL_LVL_7: return "DWELL_LVL_7"; + case BuildingID::DWELL_LVL_8: return "DWELL_LVL_8"; case BuildingID::DWELL_LVL_1_UP: return "DWELL_LVL_1_UP"; case BuildingID::DWELL_LVL_2_UP: return "DWELL_LVL_2_UP"; case BuildingID::DWELL_LVL_3_UP: return "DWELL_LVL_3_UP"; @@ -64,6 +65,7 @@ std::string defaultBuildingIdConversion(BuildingID bId) case BuildingID::DWELL_LVL_5_UP: return "DWELL_LVL_5_UP"; case BuildingID::DWELL_LVL_6_UP: return "DWELL_LVL_6_UP"; case BuildingID::DWELL_LVL_7_UP: return "DWELL_LVL_7_UP"; + case BuildingID::DWELL_LVL_8_UP: return "DWELL_LVL_8_UP"; default: return "UNKNOWN"; } diff --git a/mapeditor/inspector/towneventswidget.cpp b/mapeditor/inspector/towneventswidget.cpp index f963a2ae3..963a893f1 100644 --- a/mapeditor/inspector/towneventswidget.cpp +++ b/mapeditor/inspector/towneventswidget.cpp @@ -69,7 +69,7 @@ QVariant toVariant(const CCastleEvent& event) QVariantMap result; result["name"] = QString::fromStdString(event.name); result["message"] = QString::fromStdString(event.message.toString()); - result["players"] = QVariant::fromValue(event.players); + result["players"] = toVariant(event.players); result["humanAffected"] = QVariant::fromValue(event.humanAffected); result["computerAffected"] = QVariant::fromValue(event.computerAffected); result["firstOccurrence"] = QVariant::fromValue(event.firstOccurrence); @@ -87,7 +87,7 @@ CCastleEvent eventFromVariant(CMapHeader& map, const CGTownInstance& town, const auto v = variant.toMap(); result.name = v.value("name").toString().toStdString(); result.message.appendTextID(mapRegisterLocalizedString("map", map, TextIdentifier("town", town.instanceName, "event", result.name, "message"), v.value("message").toString().toStdString())); - result.players = v.value("players").toInt(); + result.players = playersFromVariant(v.value("players")); result.humanAffected = v.value("humanAffected").toInt(); result.computerAffected = v.value("computerAffected").toInt(); result.firstOccurrence = v.value("firstOccurrence").toInt(); diff --git a/mapeditor/mapsettings/eventsettings.cpp b/mapeditor/mapsettings/eventsettings.cpp index 9a060e50a..7f127e45f 100644 --- a/mapeditor/mapsettings/eventsettings.cpp +++ b/mapeditor/mapsettings/eventsettings.cpp @@ -16,6 +16,24 @@ #include "../../lib/constants/NumericConstants.h" #include "../../lib/constants/StringConstants.h" +QVariant toVariant(const std::set & players) +{ + QVariantList result; + for(auto const id : players) + result.push_back(QString::fromStdString(id.toString())); + return result; +} + +std::set playersFromVariant(const QVariant & v) +{ + std::set result; + + for(auto const & id : v.toList()) + result.insert(PlayerColor(PlayerColor::decode(id.toString().toStdString()))); + + return result; +} + QVariant toVariant(const TResources & resources) { QVariantMap result; @@ -30,7 +48,6 @@ TResources resourcesFromVariant(const QVariant & v) for(auto r : v.toMap().keys()) vJson[r.toStdString()].Integer() = v.toMap().value(r).toInt(); return TResources(vJson); - } QVariant toVariant(const CMapEvent & event) @@ -38,7 +55,7 @@ QVariant toVariant(const CMapEvent & event) QVariantMap result; result["name"] = QString::fromStdString(event.name); result["message"] = QString::fromStdString(event.message.toString()); - result["players"] = QVariant::fromValue(event.players); + result["players"] = toVariant(event.players); result["humanAffected"] = QVariant::fromValue(event.humanAffected); result["computerAffected"] = QVariant::fromValue(event.computerAffected); result["firstOccurrence"] = QVariant::fromValue(event.firstOccurrence); @@ -53,7 +70,7 @@ CMapEvent eventFromVariant(CMapHeader & mapHeader, const QVariant & variant) auto v = variant.toMap(); result.name = v.value("name").toString().toStdString(); result.message.appendTextID(mapRegisterLocalizedString("map", mapHeader, TextIdentifier("header", "event", result.name, "message"), v.value("message").toString().toStdString())); - result.players = v.value("players").toInt(); + result.players = playersFromVariant(v.value("players")); result.humanAffected = v.value("humanAffected").toInt(); result.computerAffected = v.value("computerAffected").toInt(); result.firstOccurrence = v.value("firstOccurrence").toInt(); diff --git a/mapeditor/mapsettings/eventsettings.h b/mapeditor/mapsettings/eventsettings.h index 991e09037..2d1cee00c 100644 --- a/mapeditor/mapsettings/eventsettings.h +++ b/mapeditor/mapsettings/eventsettings.h @@ -16,7 +16,10 @@ class EventSettings; } QVariant toVariant(const TResources & resources); +QVariant toVariant(const std::set & players); + TResources resourcesFromVariant(const QVariant & v); +std::set playersFromVariant(const QVariant & v); class EventSettings : public AbstractSettings { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index dc54e96d4..553a152da 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -584,11 +584,6 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack reinitScripting(); } -static bool evntCmp(const CMapEvent &a, const CMapEvent &b) -{ - return a.earlierThan(b); -} - void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false) {// bool forced = true - if creature should be replaced, if false - only if no creature was set @@ -599,12 +594,12 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa return; } - if (forced || town->creatures.at(GameConstants::CREATURES_PER_TOWN).second.empty())//we need to change creature + if (forced || town->creatures.at(town->town->creatures.size()).second.empty())//we need to change creature { SetAvailableCreatures ssi; ssi.tid = town->id; ssi.creatures = town->creatures; - ssi.creatures[GameConstants::CREATURES_PER_TOWN].second.clear();//remove old one + ssi.creatures[town->town->creatures.size()].second.clear();//remove old one const std::vector > &dwellings = p->dwellings; if (dwellings.empty())//no dwellings - just remove @@ -620,13 +615,13 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa if (clear) { - ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = std::max(1, (VLC->creh->objects.at(creatureId)->getGrowth())/2); + ssi.creatures[town->town->creatures.size()].first = std::max(1, (VLC->creh->objects.at(creatureId)->getGrowth())/2); } else { - ssi.creatures[GameConstants::CREATURES_PER_TOWN].first = VLC->creh->objects.at(creatureId)->getGrowth(); + ssi.creatures[town->town->creatures.size()].first = VLC->creh->objects.at(creatureId)->getGrowth(); } - ssi.creatures[GameConstants::CREATURES_PER_TOWN].second.push_back(creatureId); + ssi.creatures[town->town->creatures.size()].second.push_back(creatureId); sendAndApply(&ssi); } } @@ -635,6 +630,11 @@ void CGameHandler::onPlayerTurnStarted(PlayerColor which) { events::PlayerGotTurn::defaultExecute(serverEventBus.get(), which); turnTimerHandler->onPlayerGetTurn(which); + + handleTimeEvents(which); + + for (auto t : getPlayerState(which)->towns) + handleTownEvents(t); } void CGameHandler::onPlayerTurnEnded(PlayerColor which) @@ -671,7 +671,7 @@ void CGameHandler::onPlayerTurnEnded(PlayerColor which) void CGameHandler::addStatistics(StatisticDataSet &stat) { - for (auto & elem : gs->players) + for (const auto & elem : gs->players) { if (elem.first == PlayerColor::NEUTRAL || !elem.first.isValidPlayer()) continue; @@ -711,7 +711,7 @@ void CGameHandler::onNewTurn() addStatistics(gameState()->statistic); // write at end of turn } - for (auto & player : gs->players) + for (const auto & player : gs->players) { if (player.second.status != EPlayerStatus::INGAME) continue; @@ -864,7 +864,6 @@ void CGameHandler::onNewTurn() for (CGTownInstance *t : gs->map->towns) { PlayerColor player = t->tempOwner; - handleTownEvents(t, n); if (newWeek) //first day of week { if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING)) @@ -881,7 +880,7 @@ void CGameHandler::onNewTurn() } auto & sac = n.cres.at(t->id); - for (int k=0; k < GameConstants::CREATURES_PER_TOWN; k++) //creature growths + for (int k=0; k < t->town->creatures.size(); k++) //creature growths { if (!t->creatures.at(k).second.empty()) // there are creatures at this level { @@ -1021,7 +1020,6 @@ void CGameHandler::onNewTurn() checkVictoryLossConditionsForAll(); // check for map turn limit logGlobal->trace("Info about turn %d has been sent!", n.day); - handleTimeEvents(); //call objects for (auto & elem : gs->map->objects) { @@ -2382,8 +2380,8 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, { if(buildingID >= BuildingID::DWELL_FIRST) //dwelling { - int level = (buildingID - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN; - int upgradeNumber = (buildingID - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN; + int level = BuildingID::getLevelFromDwelling(buildingID); + int upgradeNumber = BuildingID::getUpgradedFromDwelling(buildingID); if(upgradeNumber >= t->town->creatures.at(level).size()) { @@ -3410,148 +3408,87 @@ bool CGameHandler::queryReply(QueryID qid, std::optional answer, Player return true; } -void CGameHandler::handleTimeEvents() +void CGameHandler::handleTimeEvents(PlayerColor color) { - gs->map->events.sort(evntCmp); - while(gs->map->events.size() && gs->map->events.front().firstOccurrence+1 == gs->day) + for (auto const & event : gs->map->events) { - CMapEvent ev = gs->map->events.front(); + if (!event.occursToday(gs->day)) + continue; - for (int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++) + if (!event.affectsPlayer(color, getPlayerState(color)->isHuman())) + continue; + + InfoWindow iw; + iw.player = color; + iw.text = event.message; + + //give resources + if (!event.resources.empty()) { - auto color = PlayerColor(player); - - const PlayerState * pinfo = getPlayerState(color, false); //do not output error if player does not exist - - if (pinfo //player exists - && (ev.players & 1<human) - || (ev.humanAffected && pinfo->human) - ) - ) - { - //give resources - giveResources(color, ev.resources); - - //prepare dialog - InfoWindow iw; - iw.player = color; - iw.text = ev.message; - - for (GameResID i : GameResID::ALL_RESOURCES()) - { - if (ev.resources[i]) //if resource is changed, we add it to the dialog - iw.components.emplace_back(ComponentType::RESOURCE, i, ev.resources[i]); - } - - sendAndApply(&iw); //show dialog - } - } //PLAYERS LOOP - - if (ev.nextOccurrence) - { - gs->map->events.pop_front(); - - ev.firstOccurrence += ev.nextOccurrence; - auto it = gs->map->events.begin(); - while(it != gs->map->events.end() && it->earlierThanOrEqual(ev)) - it++; - gs->map->events.insert(it, ev); - } - else - { - gs->map->events.pop_front(); + giveResources(color, event.resources); + for (GameResID i : GameResID::ALL_RESOURCES()) + if (event.resources[i]) + iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]); } + sendAndApply(&iw); //show dialog } - - //TODO send only if changed - UpdateMapEvents ume; - ume.events = gs->map->events; - sendAndApply(&ume); } -void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n) +void CGameHandler::handleTownEvents(CGTownInstance * town) { - std::sort(town->events.begin(), town->events.end(), evntCmp); - while(town->events.size() && town->events.front().firstOccurrence == gs->day) + for (auto const & event : town->events) { - PlayerColor player = town->tempOwner; - CCastleEvent ev = town->events.front(); - const PlayerState * pinfo = getPlayerState(player, false); + if (!event.occursToday(gs->day)) + continue; - if (pinfo //player exists - && (ev.players & 1<human) - || (ev.humanAffected && pinfo->human))) + PlayerColor player = town->getOwner(); + if (!event.affectsPlayer(player, getPlayerState(player)->isHuman())) + continue; + + // dialog + InfoWindow iw; + iw.player = player; + iw.text = event.message; + + if (event.resources.nonZero()) { - // dialog - InfoWindow iw; - iw.player = player; - iw.text = ev.message; + giveResources(player, event.resources); - if (ev.resources.nonZero()) + for (GameResID i : GameResID::ALL_RESOURCES()) + if (event.resources[i]) + iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]); + } + + for (auto & i : event.buildings) + { + // Only perform action if: + // 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress + // 2. Building was not built yet + // othervice, silently ignore / skip it + if (town->town->buildings.count(i) && !town->hasBuilt(i)) { - TResources was = n.res[player]; - n.res[player] += ev.resources; - n.res[player].amax(0); - - for (GameResID i : GameResID::ALL_RESOURCES()) - if (ev.resources[i] && pinfo->resources[i] != n.res.at(player)[i]) //if resource had changed, we add it to the dialog - iw.components.emplace_back(ComponentType::RESOURCE, i, n.res.at(player)[i] - was[i]); + buildStructure(town->id, i, true); + iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i)); } + } - for (auto & i : ev.buildings) + if (!event.creatures.empty()) + { + SetAvailableCreatures sac; + sac.tid = town->id; + sac.creatures = town->creatures; + + for (si32 i=0;itown->buildings.count(i) && !town->hasBuilt(i)) + if (!town->creatures.at(i).second.empty() && event.creatures.at(i) > 0)//there is dwelling { - buildStructure(town->id, i, true); - iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i)); + sac.creatures[i].first += event.creatures.at(i); + iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), event.creatures.at(i)); } } - - if (!ev.creatures.empty() && !vstd::contains(n.cres, town->id)) - { - n.cres[town->id].tid = town->id; - n.cres[town->id].creatures = town->creatures; - } - auto & sac = n.cres[town->id]; - - for (si32 i=0;icreatures.at(i).second.empty() && ev.creatures.at(i) > 0)//there is dwelling - { - sac.creatures[i].first += ev.creatures.at(i); - iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), ev.creatures.at(i)); - } - } - sendAndApply(&iw); //show dialog - } - - if (ev.nextOccurrence) - { - town->events.erase(town->events.begin()); - - ev.firstOccurrence += ev.nextOccurrence; - auto it = town->events.begin(); - while(it != town->events.end() && it->earlierThanOrEqual(ev)) - it++; - town->events.insert(it, ev); - } - else - { - town->events.erase(town->events.begin()); } + sendAndApply(&iw); //show dialog } - - //TODO send only if changed - UpdateCastleEvents uce; - uce.town = town->id; - uce.events = town->events; - sendAndApply(&uce); } bool CGameHandler::complain(const std::string &problem) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index fc522c24d..b37bf70bc 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -230,8 +230,8 @@ public: void onNewTurn(); void addStatistics(StatisticDataSet &stat); - void handleTimeEvents(); - void handleTownEvents(CGTownInstance *town, NewTurn &n); + void handleTimeEvents(PlayerColor player); + void handleTownEvents(CGTownInstance *town); bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h ); void objectVisitEnded(const CObjectVisitQuery &query); diff --git a/server/TurnTimerHandler.cpp b/server/TurnTimerHandler.cpp index b8dd196ed..a694e1df6 100644 --- a/server/TurnTimerHandler.cpp +++ b/server/TurnTimerHandler.cpp @@ -256,7 +256,7 @@ void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime) if (!si->turnTimerInfo.isBattleEnabled()) return; - ui8 side = 0; + BattleSide side = BattleSide::NONE; const CStack * stack = nullptr; bool isTactisPhase = gs->getBattle(battleID)->battleTacticDist() > 0; diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index 945ee960c..3e115d36f 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -65,7 +65,7 @@ bool BattleActionProcessor::doRetreatAction(const CBattleInfoCallback & battle, return false; } - owner->setBattleResult(battle, EBattleResult::ESCAPE, !ba.side); + owner->setBattleResult(battle, EBattleResult::ESCAPE, battle.otherSide(ba.side)); return true; } @@ -86,7 +86,7 @@ bool BattleActionProcessor::doSurrenderAction(const CBattleInfoCallback & battle } gameHandler->giveResource(player, EGameResID::GOLD, -cost); - owner->setBattleResult(battle, EBattleResult::SURRENDER, !ba.side); + owner->setBattleResult(battle, EBattleResult::SURRENDER, battle.otherSide(ba.side)); return true; } @@ -1574,7 +1574,7 @@ bool BattleActionProcessor::makeAutomaticBattleAction(const CBattleInfoCallback bool BattleActionProcessor::makePlayerBattleAction(const CBattleInfoCallback & battle, PlayerColor player, const BattleAction &ba) { - if (ba.side != 0 && ba.side != 1 && gameHandler->complain("Can not make action - invalid battle side!")) + if (ba.side != BattleSide::ATTACKER && ba.side != BattleSide::DEFENDER && gameHandler->complain("Can not make action - invalid battle side!")) return false; if(battle.battleGetTacticDist() != 0) diff --git a/server/battles/BattleFlowProcessor.cpp b/server/battles/BattleFlowProcessor.cpp index e16da4ba3..c4c1235db 100644 --- a/server/battles/BattleFlowProcessor.cpp +++ b/server/battles/BattleFlowProcessor.cpp @@ -34,7 +34,7 @@ BattleFlowProcessor::BattleFlowProcessor(BattleProcessor * owner, CGameHandler * { } -void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard +void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard { int x = targetPosition.getX(); int y = targetPosition.getY(); @@ -185,7 +185,7 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle, void BattleFlowProcessor::castOpeningSpells(const CBattleInfoCallback & battle) { - for (int i = 0; i < 2; ++i) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { auto h = battle.battleGetFightingHero(i); if (!h) @@ -742,7 +742,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c return b->subtype.as() == SpellID::NONE; }); - int side = *battle.playerToSide(st->unitOwner()); + BattleSide side = battle.playerToSide(st->unitOwner()); if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0) { bool cast = false; diff --git a/server/battles/BattleFlowProcessor.h b/server/battles/BattleFlowProcessor.h index e781b0a75..70ef29249 100644 --- a/server/battles/BattleFlowProcessor.h +++ b/server/battles/BattleFlowProcessor.h @@ -9,6 +9,8 @@ */ #pragma once +#include "../lib/battle/BattleSide.h" + VCMI_LIB_NAMESPACE_BEGIN class CStack; struct BattleHex; @@ -35,7 +37,7 @@ class BattleFlowProcessor : boost::noncopyable bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack); bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack); - void summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex); + void summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex); void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack); void tryPlaceMoats(const CBattleInfoCallback & battle); void castOpeningSpells(const CBattleInfoCallback & battle); diff --git a/server/battles/BattleProcessor.cpp b/server/battles/BattleProcessor.cpp index 14f359397..660e9d317 100644 --- a/server/battles/BattleProcessor.cpp +++ b/server/battles/BattleProcessor.cpp @@ -57,20 +57,18 @@ void BattleProcessor::restartBattlePrimary(const BattleID & battleID, const CArm { auto battle = gameHandler->gameState()->getBattle(battleID); - auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->sides[0].color)); + auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->getSide(BattleSide::ATTACKER).color)); if(!lastBattleQuery) - lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->sides[1].color)); + lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->getSide(BattleSide::DEFENDER).color)); assert(lastBattleQuery); //existing battle query for retying auto-combat if(lastBattleQuery) { - const CGHeroInstance*heroes[2]; - heroes[0] = hero1; - heroes[1] = hero2; + BattleSideArray heroes{hero1, hero2}; - for(int i : {0, 1}) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) { if(heroes[i]) { @@ -83,8 +81,8 @@ void BattleProcessor::restartBattlePrimary(const BattleID & battleID, const CArm lastBattleQuery->result = std::nullopt; - assert(lastBattleQuery->belligerents[0] == battle->sides[0].armyObject); - assert(lastBattleQuery->belligerents[1] == battle->sides[1].armyObject); + assert(lastBattleQuery->belligerents[BattleSide::ATTACKER] == battle->getSide(BattleSide::ATTACKER).armyObject); + assert(lastBattleQuery->belligerents[BattleSide::DEFENDER] == battle->getSide(BattleSide::DEFENDER).armyObject); } BattleCancelled bc; @@ -101,12 +99,8 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm assert(gameHandler->gameState()->getBattle(army1->getOwner()) == nullptr); assert(gameHandler->gameState()->getBattle(army2->getOwner()) == nullptr); - const CArmedInstance *armies[2]; - armies[0] = army1; - armies[1] = army2; - const CGHeroInstance*heroes[2]; - heroes[0] = hero1; - heroes[1] = hero2; + BattleSideArray armies{army1, army2}; + BattleSideArrayheroes{hero1, hero2}; auto battleID = setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces @@ -126,9 +120,9 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm } } - auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->sides[0].color)); + auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->getSide(BattleSide::ATTACKER).color)); if(!lastBattleQuery) - lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->sides[1].color)); + lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle->getSide(BattleSide::DEFENDER).color)); if (lastBattleQuery) { @@ -139,7 +133,7 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm auto newBattleQuery = std::make_shared(gameHandler, battle); // store initial mana to reset if battle has been restarted - for(int i : {0, 1}) + for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) if(heroes[i]) newBattleQuery->initialHeroMana[i] = heroes[i]->mana; @@ -162,7 +156,7 @@ void BattleProcessor::startBattleI(const CArmedInstance *army1, const CArmedInst startBattleI(army1, army2, army2->visitablePos(), creatureBank); } -BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town) +BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray armies, BattleSideArray heroes, bool creatureBank, const CGTownInstance *town) { const auto & t = *gameHandler->getTile(tile); TerrainId terrain = t.terType->getId(); @@ -170,7 +164,7 @@ BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2] terrain = ETerrainId::SAND; BattleField terType = gameHandler->gameState()->battleGetBattlefieldType(tile, gameHandler->getRandomGenerator()); - if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) + if (heroes[BattleSide::ATTACKER] && heroes[BattleSide::ATTACKER]->boat && heroes[BattleSide::DEFENDER] && heroes[BattleSide::DEFENDER]->boat) terType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.ship_to_ship")); //send info about battles @@ -178,14 +172,14 @@ BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2] bs.info = BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town); bs.battleID = gameHandler->gameState()->nextBattleID; - engageIntoBattle(bs.info->sides[0].color); - engageIntoBattle(bs.info->sides[1].color); + engageIntoBattle(bs.info->getSide(BattleSide::ATTACKER).color); + engageIntoBattle(bs.info->getSide(BattleSide::DEFENDER).color); - auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(bs.info->sides[0].color)); + auto lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(bs.info->getSide(BattleSide::ATTACKER).color)); if(!lastBattleQuery) - lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(bs.info->sides[1].color)); - bool isDefenderHuman = bs.info->sides[1].color.isValidPlayer() && gameHandler->getPlayerState(bs.info->sides[1].color)->isHuman(); - bool isAttackerHuman = gameHandler->getPlayerState(bs.info->sides[0].color)->isHuman(); + lastBattleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(bs.info->getSide(BattleSide::DEFENDER).color)); + bool isDefenderHuman = bs.info->getSide(BattleSide::DEFENDER).color.isValidPlayer() && gameHandler->getPlayerState(bs.info->getSide(BattleSide::DEFENDER).color)->isHuman(); + bool isAttackerHuman = gameHandler->getPlayerState(bs.info->getSide(BattleSide::ATTACKER).color)->isHuman(); bool onlyOnePlayerHuman = isDefenderHuman != isAttackerHuman; bs.info->replayAllowed = lastBattleQuery == nullptr && onlyOnePlayerHuman; @@ -284,7 +278,7 @@ bool BattleProcessor::makePlayerBattleAction(const BattleID & battleID, PlayerCo return result; } -void BattleProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide) +void BattleProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, BattleSide victoriusSide) { resultProcessor->setBattleResult(battle, resultType, victoriusSide); resultProcessor->endBattle(battle); diff --git a/server/battles/BattleProcessor.h b/server/battles/BattleProcessor.h index e284def89..c21dd6a66 100644 --- a/server/battles/BattleProcessor.h +++ b/server/battles/BattleProcessor.h @@ -10,6 +10,7 @@ #pragma once #include "../../lib/GameConstants.h" +#include "../../lib/battle/BattleSide.h" VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; @@ -44,11 +45,11 @@ class BattleProcessor : boost::noncopyable void engageIntoBattle(PlayerColor player); bool checkBattleStateChanges(const CBattleInfoCallback & battle); - BattleID setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town); + BattleID setupBattle(int3 tile, BattleSideArray armies, BattleSideArray heroes, bool creatureBank, const CGTownInstance *town); bool makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba); - void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide); + void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, BattleSide victoriusSide); public: explicit BattleProcessor(CGameHandler * gameHandler); diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 4100fca32..0f75eb105 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -38,7 +38,7 @@ BattleResultProcessor::BattleResultProcessor(BattleProcessor * owner, CGameHandl { } -CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle): +CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, BattleSide sideInBattle): army(battle.battleGetArmyObject(sideInBattle)) { heroWithDeadCommander = ObjectInstanceID(); @@ -205,25 +205,18 @@ FinishingBattleHelper::FinishingBattleHelper(const CBattleInfoCallback & info, c this->remainingBattleQueriesCount = remainingBattleQueriesCount; } -//FinishingBattleHelper::FinishingBattleHelper() -//{ -// winnerHero = loserHero = nullptr; -// winnerSide = 0; -// remainingBattleQueriesCount = 0; -//} - void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle) { - auto const & giveExp = [](BattleResult &r) + auto const & giveExp = [&battle](BattleResult &r) { - if (r.winner > 1) + if (r.winner == BattleSide::NONE) { // draw return; } - r.exp[0] = 0; - r.exp[1] = 0; - for (auto i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++) + r.exp[BattleSide::ATTACKER] = 0; + r.exp[BattleSide::DEFENDER] = 0; + for (auto i = r.casualties[battle.otherSide(r.winner)].begin(); i!=r.casualties[battle.otherSide(r.winner)].end(); i++) { r.exp[r.winner] += VLC->creh->objects.at(i->first)->valOfBonuses(BonusType::STACK_HEALTH) * i->second; } @@ -241,9 +234,9 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle) if (battleResult->result == EBattleResult::NORMAL) // give 500 exp for defeating hero, unless he escaped { if(heroAttacker) - battleResult->exp[1] += 500; + battleResult->exp[BattleSide::DEFENDER] += 500; if(heroDefender) - battleResult->exp[0] += 500; + battleResult->exp[BattleSide::ATTACKER] += 500; } // Give 500 exp to winner if a town was conquered during the battle @@ -252,17 +245,17 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle) battleResult->exp[BattleSide::ATTACKER] += 500; if(heroAttacker) - battleResult->exp[0] = heroAttacker->calculateXp(battleResult->exp[0]);//scholar skill + battleResult->exp[BattleSide::ATTACKER] = heroAttacker->calculateXp(battleResult->exp[BattleSide::ATTACKER]);//scholar skill if(heroDefender) - battleResult->exp[1] = heroDefender->calculateXp(battleResult->exp[1]); + battleResult->exp[BattleSide::DEFENDER] = heroDefender->calculateXp(battleResult->exp[BattleSide::DEFENDER]); - auto battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(0))); + auto battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(BattleSide::ATTACKER))); if(!battleQuery) - battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(1))); + battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(BattleSide::DEFENDER))); if (!battleQuery) { logGlobal->error("Cannot find battle query!"); - gameHandler->complain("Player " + boost::lexical_cast(battle.sideToPlayer(0)) + " has no battle query at the top!"); + gameHandler->complain("Player " + boost::lexical_cast(battle.sideToPlayer(BattleSide::ATTACKER)) + " has no battle query at the top!"); return; } @@ -307,9 +300,9 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle) void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) { - auto battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(0))); + auto battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(BattleSide::ATTACKER))); if(!battleQuery) - battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(1))); + battleQuery = std::dynamic_pointer_cast(gameHandler->queries->topQuery(battle.sideToPlayer(BattleSide::DEFENDER))); if(!battleQuery) { logGlobal->trace("No battle query, battle end was confirmed by another player"); @@ -508,29 +501,29 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) } // add statistic - if(battle.sideToPlayer(0) == PlayerColor::NEUTRAL || battle.sideToPlayer(1) == PlayerColor::NEUTRAL) + if(battle.sideToPlayer(BattleSide::ATTACKER) == PlayerColor::NEUTRAL || battle.sideToPlayer(BattleSide::DEFENDER) == PlayerColor::NEUTRAL) { - gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(0)].numBattlesNeutral++; - gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(1)].numBattlesNeutral++; + gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesNeutral++; + gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesNeutral++; if(!finishingBattle->isDraw()) gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesNeutral++; } else { - gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(0)].numBattlesPlayer++; - gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(1)].numBattlesPlayer++; + gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesPlayer++; + gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesPlayer++; if(!finishingBattle->isDraw()) gameHandler->gameState()->statistic.accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesPlayer++; } BattleResultAccepted raccepted; raccepted.battleID = battle.getBattle()->getBattleID(); - raccepted.heroResult[0].army = const_cast(battle.battleGetArmyObject(BattleSide::ATTACKER)); - raccepted.heroResult[1].army = const_cast(battle.battleGetArmyObject(BattleSide::DEFENDER)); - raccepted.heroResult[0].hero = const_cast(battle.battleGetFightingHero(BattleSide::ATTACKER)); - raccepted.heroResult[1].hero = const_cast(battle.battleGetFightingHero(BattleSide::DEFENDER)); - raccepted.heroResult[0].exp = battleResult->exp[0]; - raccepted.heroResult[1].exp = battleResult->exp[1]; + raccepted.heroResult[BattleSide::ATTACKER].army = const_cast(battle.battleGetArmyObject(BattleSide::ATTACKER)); + raccepted.heroResult[BattleSide::DEFENDER].army = const_cast(battle.battleGetArmyObject(BattleSide::DEFENDER)); + raccepted.heroResult[BattleSide::ATTACKER].hero = const_cast(battle.battleGetFightingHero(BattleSide::ATTACKER)); + raccepted.heroResult[BattleSide::DEFENDER].hero = const_cast(battle.battleGetFightingHero(BattleSide::DEFENDER)); + raccepted.heroResult[BattleSide::ATTACKER].exp = battleResult->exp[BattleSide::ATTACKER]; + raccepted.heroResult[BattleSide::DEFENDER].exp = battleResult->exp[BattleSide::DEFENDER]; raccepted.winnerSide = finishingBattle->winnerSide; gameHandler->sendAndApply(&raccepted); @@ -593,7 +586,7 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleID & battleID, const gameHandler->heroPool->onHeroEscaped(finishingBattle->loser, finishingBattle->loserHero); } - if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty() + if (result.winner != BattleSide::NONE && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty() && (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive)) { RemoveObject ro(finishingBattle->winnerHero->id, finishingBattle->winnerHero->getOwner()); @@ -607,7 +600,7 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleID & battleID, const battleResults.erase(battleID); } -void BattleResultProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide) +void BattleResultProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, BattleSide victoriusSide) { assert(battleResults.count(battle.getBattle()->getBattleID()) == 0); diff --git a/server/battles/BattleResultProcessor.h b/server/battles/BattleResultProcessor.h index f82f18fba..2e5e73bd1 100644 --- a/server/battles/BattleResultProcessor.h +++ b/server/battles/BattleResultProcessor.h @@ -12,6 +12,7 @@ #include "../../lib/GameConstants.h" #include "../../lib/networkPacks/StackLocation.h" #include "../../lib/networkPacks/ArtifactLocation.h" +#include "../../lib/battle/BattleSide.h" VCMI_LIB_NAMESPACE_BEGIN struct SideInBattle; @@ -34,7 +35,7 @@ struct CasualtiesAfterBattle TSummoned summoned; ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations - CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle); + CasualtiesAfterBattle(const CBattleInfoCallback & battle, BattleSide sideInBattle); void updateArmy(CGameHandler * gh); }; @@ -42,11 +43,11 @@ struct FinishingBattleHelper { FinishingBattleHelper(const CBattleInfoCallback & battle, const BattleResult & result, int RemainingBattleQueriesCount); - inline bool isDraw() const {return winnerSide == 2;} + inline bool isDraw() const {return winnerSide == BattleSide::NONE;} const CGHeroInstance *winnerHero, *loserHero; PlayerColor victor, loser; - ui8 winnerSide; + BattleSide winnerSide; int remainingBattleQueriesCount; @@ -74,7 +75,7 @@ public: bool battleIsEnding(const CBattleInfoCallback & battle) const; - void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide); + void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, BattleSide victoriusSide); void endBattle(const CBattleInfoCallback & battle); //ends battle void endBattleConfirm(const CBattleInfoCallback & battle); void battleAfterLevelUp(const BattleID & battleID, const BattleResult & result); diff --git a/server/queries/BattleQueries.cpp b/server/queries/BattleQueries.cpp index 485f1f9fa..4fc0c25f0 100644 --- a/server/queries/BattleQueries.cpp +++ b/server/queries/BattleQueries.cpp @@ -35,17 +35,18 @@ CBattleQuery::CBattleQuery(CGameHandler * owner, const IBattleInfo * bi): CQuery(owner), battleID(bi->getBattleID()) { - belligerents[0] = bi->getSideArmy(0); - belligerents[1] = bi->getSideArmy(1); + belligerents[BattleSide::ATTACKER] = bi->getSideArmy(BattleSide::ATTACKER); + belligerents[BattleSide::DEFENDER] = bi->getSideArmy(BattleSide::DEFENDER); - addPlayer(bi->getSidePlayer(0)); - addPlayer(bi->getSidePlayer(1)); + addPlayer(bi->getSidePlayer(BattleSide::ATTACKER)); + addPlayer(bi->getSidePlayer(BattleSide::DEFENDER)); } CBattleQuery::CBattleQuery(CGameHandler * owner): CQuery(owner) { - belligerents[0] = belligerents[1] = nullptr; + belligerents[BattleSide::ATTACKER] = nullptr; + belligerents[BattleSide::DEFENDER] = nullptr; } bool CBattleQuery::blocksPack(const CPack * pack) const @@ -81,8 +82,8 @@ CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * bi(bi), result(Br) { - addPlayer(bi->getSidePlayer(0)); - addPlayer(bi->getSidePlayer(1)); + addPlayer(bi->getSidePlayer(BattleSide::ATTACKER)); + addPlayer(bi->getSidePlayer(BattleSide::DEFENDER)); } void CBattleDialogQuery::onRemoval(PlayerColor color) @@ -97,11 +98,11 @@ void CBattleDialogQuery::onRemoval(PlayerColor color) { gh->battles->restartBattlePrimary( bi->getBattleID(), - bi->getSideArmy(0), - bi->getSideArmy(1), + bi->getSideArmy(BattleSide::ATTACKER), + bi->getSideArmy(BattleSide::DEFENDER), bi->getLocation(), - bi->getSideHero(0), - bi->getSideHero(1), + bi->getSideHero(BattleSide::ATTACKER), + bi->getSideHero(BattleSide::DEFENDER), bi->isCreatureBank(), bi->getDefendedTown() ); diff --git a/server/queries/BattleQueries.h b/server/queries/BattleQueries.h index cae8c3b09..6916cb7f5 100644 --- a/server/queries/BattleQueries.h +++ b/server/queries/BattleQueries.h @@ -11,6 +11,7 @@ #include "CQuery.h" #include "../../lib/networkPacks/PacksForClientBattle.h" +#include "../../lib/battle/BattleSide.h" VCMI_LIB_NAMESPACE_BEGIN class IBattleInfo; @@ -20,8 +21,8 @@ VCMI_LIB_NAMESPACE_END class CBattleQuery : public CQuery { public: - std::array belligerents; - std::array initialHeroMana; + BattleSideArray belligerents; + BattleSideArray initialHeroMana; BattleID battleID; std::optional result; diff --git a/test/battle/BattleHexTest.cpp b/test/battle/BattleHexTest.cpp index d2179587d..075952bba 100644 --- a/test/battle/BattleHexTest.cpp +++ b/test/battle/BattleHexTest.cpp @@ -92,16 +92,16 @@ TEST(BattleHexTest, getClosestTile) possibilities.insert(119); possibilities.insert(186); - EXPECT_EQ(mainHex.getClosestTile(0,mainHex,possibilities), 3); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::ATTACKER,mainHex,possibilities), 3); mainHex = 139; - EXPECT_EQ(mainHex.getClosestTile(1,mainHex,possibilities), 119); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::DEFENDER,mainHex,possibilities), 119); mainHex = 16; - EXPECT_EQ(mainHex.getClosestTile(1,mainHex,possibilities), 100); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::DEFENDER,mainHex,possibilities), 100); mainHex = 166; - EXPECT_EQ(mainHex.getClosestTile(0,mainHex,possibilities), 186); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::ATTACKER,mainHex,possibilities), 186); mainHex = 76; - EXPECT_EQ(mainHex.getClosestTile(1,mainHex,possibilities), 3); - EXPECT_EQ(mainHex.getClosestTile(0,mainHex,possibilities), 100); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::DEFENDER,mainHex,possibilities), 3); + EXPECT_EQ(mainHex.getClosestTile(BattleSide::ATTACKER,mainHex,possibilities), 100); } TEST(BattleHexTest, moveEDir) diff --git a/test/battle/CBattleInfoCallbackTest.cpp b/test/battle/CBattleInfoCallbackTest.cpp index 8a443ef48..d7929f7ea 100644 --- a/test/battle/CBattleInfoCallbackTest.cpp +++ b/test/battle/CBattleInfoCallbackTest.cpp @@ -104,7 +104,7 @@ class UnitsFake public: std::vector> allUnits; - UnitFake & add(ui8 side) + UnitFake & add(BattleSide side) { auto * unit = new UnitFake(); EXPECT_CALL(*unit, unitSide()).WillRepeatedly(Return(side)); @@ -207,7 +207,7 @@ public: class AttackableHexesTest : public CBattleInfoCallbackTest { public: - UnitFake & addRegularMelee(BattleHex hex, uint8_t side) + UnitFake & addRegularMelee(BattleHex hex, BattleSide side) { auto & unit = unitsFake.add(side); @@ -219,7 +219,7 @@ public: return unit; } - UnitFake & addDragon(BattleHex hex, uint8_t side) + UnitFake & addDragon(BattleHex hex, BattleSide side) { auto & unit = addRegularMelee(hex, side); @@ -245,9 +245,9 @@ public: TEST_F(AttackableHexesTest, DragonRightRegular_RightHorithontalBreath) { // X A D # - UnitFake & attacker = addDragon(35, 0); - UnitFake & defender = addRegularMelee(36, 1); - UnitFake & next = addRegularMelee(37, 1); + UnitFake & attacker = addDragon(35, BattleSide::ATTACKER); + UnitFake & defender = addRegularMelee(36, BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(37, BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -259,9 +259,9 @@ TEST_F(AttackableHexesTest, DragonDragonBottomRightHead_BottomRightBreathFromHea // X A // D X target D // # - UnitFake & attacker = addDragon(35, 0); - UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); + UnitFake & attacker = addDragon(35, BattleSide::ATTACKER); + UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -273,9 +273,9 @@ TEST_F(AttackableHexesTest, DragonDragonVerticalDownHead_VerticalDownBreathFromH // X A // D X target D // # - UnitFake & attacker = addDragon(35, 0); - UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), 1); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); + UnitFake & attacker = addDragon(35, BattleSide::ATTACKER); + UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -287,9 +287,9 @@ TEST_F(AttackableHexesTest, DragonDragonVerticalDownHeadReverse_VerticalDownBrea // A X // X D target D // # - UnitFake & attacker = addDragon(36, 1); - UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 0); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), 0); + UnitFake & attacker = addDragon(36, BattleSide::DEFENDER); + UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::ATTACKER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::ATTACKER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -301,9 +301,9 @@ TEST_F(AttackableHexesTest, DragonDragonVerticalDownBack_VerticalDownBreath) // X A // D X target X // # - UnitFake & attacker = addDragon(37, 0); - UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), 1); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); + UnitFake & attacker = addDragon(37, BattleSide::ATTACKER); + UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex()); @@ -315,9 +315,9 @@ TEST_F(AttackableHexesTest, DragonDragonHeadBottomRight_BottomRightBreathFromHea // X A // D X target D // # - UnitFake & attacker = addDragon(37, 0); - UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), 1); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); + UnitFake & attacker = addDragon(37, BattleSide::ATTACKER); + UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -329,9 +329,9 @@ TEST_F(AttackableHexesTest, DragonVerticalDownDragonBackReverse_VerticalDownBrea // A X // X D target X // # - UnitFake & attacker = addDragon(36, 1); - UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), 0); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), 0); + UnitFake & attacker = addDragon(36, BattleSide::DEFENDER); + UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::ATTACKER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::ATTACKER); auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex()); @@ -342,9 +342,9 @@ TEST_F(AttackableHexesTest, DragonRightBottomDragonHeadReverse_RightBottomBreath { // A X // X D target D - UnitFake & attacker = addDragon(36, 1); - UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), 0); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), 0); + UnitFake & attacker = addDragon(36, BattleSide::DEFENDER); + UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::ATTACKER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::ATTACKER); auto attacked = getAttackedUnits(attacker, defender, defender.getPosition()); @@ -356,9 +356,9 @@ TEST_F(AttackableHexesTest, DragonLeftBottomDragonBackToBack_LeftBottomBreathFro // X A // D X target X // # - UnitFake & attacker = addDragon(8, 0); - UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT).cloneInDirection(BattleHex::LEFT), 1); - UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), 1); + UnitFake & attacker = addDragon(8, BattleSide::ATTACKER); + UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT).cloneInDirection(BattleHex::LEFT), BattleSide::DEFENDER); + UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER); auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex()); @@ -370,9 +370,9 @@ TEST_F(AttackableHexesTest, DefenderPositionOverride_BreathCountsHypoteticDefend // # N // X D target D // A X - UnitFake & attacker = addDragon(35, 1); - UnitFake & defender = addDragon(8, 0); - UnitFake & next = addDragon(2, 0); + UnitFake & attacker = addDragon(35, BattleSide::DEFENDER); + UnitFake & defender = addDragon(8, BattleSide::ATTACKER); + UnitFake & next = addDragon(2, BattleSide::ATTACKER); startBattle(); redirectUnitsToFake(); @@ -402,10 +402,10 @@ public: auto ret = subject.battleIsFinished(); EXPECT_TRUE(ret); - EXPECT_EQ(*ret, 2); + EXPECT_EQ(*ret, BattleSide::NONE); } - void expectBattleWinner(ui8 side) + void expectBattleWinner(BattleSide side) { auto ret = subject.battleIsFinished(); @@ -413,12 +413,12 @@ public: EXPECT_EQ(*ret, side); } - void expectBattleLooser(ui8 side) + void expectBattleLooser(BattleSide side) { auto ret = subject.battleIsFinished(); EXPECT_TRUE(ret); - EXPECT_NE(*ret, (int)side); + EXPECT_NE(*ret, side); } void setDefaultExpectations() @@ -443,21 +443,21 @@ TEST_F(BattleFinishedTest, EmptyBattleIsDraw) TEST_F(BattleFinishedTest, LastAliveUnitWins) { - UnitFake & unit = unitsFake.add(1); + UnitFake & unit = unitsFake.add(BattleSide::DEFENDER); unit.makeAlive(); unit.setDefaultState(); setDefaultExpectations(); startBattle(); - expectBattleWinner(1); + expectBattleWinner(BattleSide::DEFENDER); } TEST_F(BattleFinishedTest, TwoUnitsContinueFight) { - UnitFake & unit1 = unitsFake.add(0); + UnitFake & unit1 = unitsFake.add(BattleSide::ATTACKER); unit1.makeAlive(); - UnitFake & unit2 = unitsFake.add(1); + UnitFake & unit2 = unitsFake.add(BattleSide::DEFENDER); unit2.makeAlive(); setDefaultExpectations(); @@ -468,7 +468,7 @@ TEST_F(BattleFinishedTest, TwoUnitsContinueFight) TEST_F(BattleFinishedTest, LastWarMachineNotWins) { - UnitFake & unit = unitsFake.add(0); + UnitFake & unit = unitsFake.add(BattleSide::ATTACKER); unit.makeAlive(); unit.makeWarMachine(); unit.setDefaultState(); @@ -476,18 +476,18 @@ TEST_F(BattleFinishedTest, LastWarMachineNotWins) setDefaultExpectations(); startBattle(); - expectBattleLooser(0); + expectBattleLooser(BattleSide::ATTACKER); } TEST_F(BattleFinishedTest, LastWarMachineLoose) { try { - UnitFake & unit1 = unitsFake.add(0); + UnitFake & unit1 = unitsFake.add(BattleSide::ATTACKER); unit1.makeAlive(); unit1.setDefaultState(); - UnitFake & unit2 = unitsFake.add(1); + UnitFake & unit2 = unitsFake.add(BattleSide::DEFENDER); unit2.makeAlive(); unit2.makeWarMachine(); unit2.setDefaultState(); @@ -495,7 +495,7 @@ TEST_F(BattleFinishedTest, LastWarMachineLoose) setDefaultExpectations(); startBattle(); - expectBattleWinner(0); + expectBattleWinner(BattleSide::ATTACKER); } catch(const std::exception & e) { diff --git a/test/battle/battle_UnitTest.cpp b/test/battle/battle_UnitTest.cpp index 390c35a29..b6ead9ab2 100644 --- a/test/battle/battle_UnitTest.cpp +++ b/test/battle/battle_UnitTest.cpp @@ -15,7 +15,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWide) { BattleHex position(77); - auto actual = battle::Unit::getSurroundingHexes(position, false, 0); + auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); EXPECT_EQ(actual, position.neighbouringTiles()); } @@ -24,7 +24,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWideLeftCorner) { BattleHex position(34); - auto actual = battle::Unit::getSurroundingHexes(position, false, 0); + auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); EXPECT_EQ(actual, position.neighbouringTiles()); } @@ -33,7 +33,7 @@ TEST(battle_Unit_getSurroundingHexes, oneWideRightCorner) { BattleHex position(117); - auto actual = battle::Unit::getSurroundingHexes(position, false, 0); + auto actual = battle::Unit::getSurroundingHexes(position, false, BattleSide::ATTACKER); EXPECT_EQ(actual, position.neighbouringTiles()); } diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index 941397fdc..8acd3bbc9 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -188,8 +188,8 @@ public: void startTestBattle(const CGHeroInstance * attacker, const CGHeroInstance * defender) { - const CGHeroInstance * heroes[2] = {attacker, defender}; - const CArmedInstance * armedInstancies[2] = {attacker, defender}; + BattleSideArray heroes = {attacker, defender}; + BattleSideArray armedInstancies = {attacker, defender}; int3 tile(4,4,0); diff --git a/test/mock/BattleFake.cpp b/test/mock/BattleFake.cpp index c22e5e3a4..3a5c6488c 100644 --- a/test/mock/BattleFake.cpp +++ b/test/mock/BattleFake.cpp @@ -44,7 +44,7 @@ void UnitFake::expectAnyBonusSystemCall() EXPECT_CALL(*this, getTreeVersion()).Times(AtLeast(0)); } -UnitFake & UnitsFake::add(ui8 side) +UnitFake & UnitsFake::add(BattleSide side) { auto * unit = new UnitFake(); ON_CALL(*unit, unitSide()).WillByDefault(Return(side)); diff --git a/test/mock/BattleFake.h b/test/mock/BattleFake.h index 4a8d04b4d..42ff10e07 100644 --- a/test/mock/BattleFake.h +++ b/test/mock/BattleFake.h @@ -52,7 +52,7 @@ class UnitsFake public: std::vector> allUnits; - UnitFake & add(ui8 side); + UnitFake & add(BattleSide side); battle::Units getUnitsIf(battle::UnitFilter predicate) const; diff --git a/test/mock/mock_IBattleInfoCallback.h b/test/mock/mock_IBattleInfoCallback.h index f28dd37f6..f2eb2b35b 100644 --- a/test/mock/mock_IBattleInfoCallback.h +++ b/test/mock/mock_IBattleInfoCallback.h @@ -22,10 +22,10 @@ public: MOCK_CONST_METHOD0(battleTerrainType, TerrainId()); MOCK_CONST_METHOD0(battleGetBattlefieldType, BattleField()); - MOCK_CONST_METHOD0(battleIsFinished, std::optional()); + MOCK_CONST_METHOD0(battleIsFinished, std::optional()); MOCK_CONST_METHOD0(battleTacticDist, si8()); - MOCK_CONST_METHOD0(battleGetTacticsSide, si8()); + MOCK_CONST_METHOD0(battleGetTacticsSide, BattleSide()); MOCK_CONST_METHOD0(battleNextUnitId, uint32_t()); diff --git a/test/mock/mock_UnitInfo.h b/test/mock/mock_UnitInfo.h index fda7aa8e1..a336fbbd5 100644 --- a/test/mock/mock_UnitInfo.h +++ b/test/mock/mock_UnitInfo.h @@ -18,7 +18,7 @@ public: MOCK_CONST_METHOD0(unitBaseAmount, int32_t()); MOCK_CONST_METHOD0(unitId, uint32_t()); - MOCK_CONST_METHOD0(unitSide, ui8()); + MOCK_CONST_METHOD0(unitSide, BattleSide()); MOCK_CONST_METHOD0(unitOwner, PlayerColor()); MOCK_CONST_METHOD0(unitSlot, SlotID()); diff --git a/test/mock/mock_battle_IBattleState.h b/test/mock/mock_battle_IBattleState.h index 6ef0998d8..0893fd6cc 100644 --- a/test/mock/mock_battle_IBattleState.h +++ b/test/mock/mock_battle_IBattleState.h @@ -25,20 +25,20 @@ public: MOCK_CONST_METHOD0(getDefendedTown, const CGTownInstance *()); MOCK_CONST_METHOD1(getWallState, EWallState(EWallPart)); MOCK_CONST_METHOD0(getGateState, EGateState()); - MOCK_CONST_METHOD1(getSidePlayer, PlayerColor(ui8)); - MOCK_CONST_METHOD1(getSideArmy, const CArmedInstance *(ui8)); - MOCK_CONST_METHOD1(getSideHero, const CGHeroInstance *(ui8)); - MOCK_CONST_METHOD1(getCastSpells, uint32_t(ui8)); - MOCK_CONST_METHOD1(getEnchanterCounter, int32_t(ui8)); + MOCK_CONST_METHOD1(getSidePlayer, PlayerColor(BattleSide)); + MOCK_CONST_METHOD1(getSideArmy, const CArmedInstance *(BattleSide)); + MOCK_CONST_METHOD1(getSideHero, const CGHeroInstance *(BattleSide)); + MOCK_CONST_METHOD1(getCastSpells, uint32_t(BattleSide)); + MOCK_CONST_METHOD1(getEnchanterCounter, int32_t(BattleSide)); MOCK_CONST_METHOD0(getTacticDist, ui8()); - MOCK_CONST_METHOD0(getTacticsSide, ui8()); + MOCK_CONST_METHOD0(getTacticsSide, BattleSide()); MOCK_CONST_METHOD0(getBonusBearer, const IBonusBearer *()); MOCK_CONST_METHOD0(nextUnitId, uint32_t()); MOCK_CONST_METHOD3(getActualDamage, int64_t(const DamageRange &, int32_t, vstd::RNG &)); MOCK_CONST_METHOD0(getBattleID, BattleID()); MOCK_CONST_METHOD0(getLocation, int3()); MOCK_CONST_METHOD0(isCreatureBank, bool()); - MOCK_CONST_METHOD1(getUsedSpells, std::vector(ui8)); + MOCK_CONST_METHOD1(getUsedSpells, std::vector(BattleSide)); MOCK_METHOD0(nextRound, void()); MOCK_METHOD1(nextTurn, void(uint32_t)); diff --git a/test/mock/mock_battle_Unit.h b/test/mock/mock_battle_Unit.h index 14d3e5dd4..3562c48ea 100644 --- a/test/mock/mock_battle_Unit.h +++ b/test/mock/mock_battle_Unit.h @@ -38,7 +38,7 @@ public: MOCK_CONST_METHOD0(unitBaseAmount, int32_t()); MOCK_CONST_METHOD0(unitId, uint32_t()); - MOCK_CONST_METHOD0(unitSide, ui8()); + MOCK_CONST_METHOD0(unitSide, BattleSide()); MOCK_CONST_METHOD0(unitOwner, PlayerColor()); MOCK_CONST_METHOD0(unitSlot, SlotID()); MOCK_CONST_METHOD0(unitType, const CCreature * ());