1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Refactored player-specific data into single struct BattleState.

This commit is contained in:
Michał W. Urbańczyk 2013-07-21 22:01:29 +00:00
parent 491bd557ef
commit d8a27d8f3c
13 changed files with 212 additions and 137 deletions

View File

@ -329,13 +329,14 @@ namespace vstd
return -1;
}
//Func(T1,T2) must say if these elements matches
template <typename T1, typename T2, typename Func>
int find_pos(const std::vector<T1> & c, const T2 &s, const Func &f)
//Func f tells if element matches
template <typename Container, typename Func>
int find_pos_if(const Container & c, const Func &f)
{
for(size_t i=0; i < c.size(); ++i)
if(f(c[i],s))
return i;
auto ret = boost::range::find_if(c, f);
if(ret != std::end(c))
return std::distance(std::begin(c), ret);
return -1;
}

View File

@ -570,51 +570,58 @@ void CClient::battleStarted(const BattleInfo * info)
{
for(auto &battleCb : battleCallbacks)
{
if(vstd::contains(info->sides, battleCb.first) || battleCb.first >= PlayerColor::PLAYER_LIMIT)
if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
|| battleCb.first >= PlayerColor::PLAYER_LIMIT)
{
battleCb.second->setBattle(info);
}
}
// for(ui8 side : info->sides)
// if(battleCallbacks.count(side))
// battleCallbacks[side]->setBattle(info);
shared_ptr<CPlayerInterface> att, def;
auto &leftSide = info->sides[0], &rightSide = info->sides[1];
//If quick combat is not, do not prepare interfaces for battleint
if(!settings["adventure"]["quickCombat"].Bool())
{
if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human)
att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[0]] );
if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human)
att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[leftSide.color] );
if(vstd::contains(playerint, info->sides[1]) && playerint[info->sides[1]]->human)
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[1]] );
if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human)
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
}
if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
{
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
new CBattleInterface(info->belligerents[0], info->belligerents[1], info->heroes[0], info->heroes[1],
new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
Rect((screen->w - 800)/2,
(screen->h - 600)/2, 800, 600), att, def);
}
if(vstd::contains(battleints,info->sides[0]))
battleints[info->sides[0]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 0);
if(vstd::contains(battleints,info->sides[1]))
battleints[info->sides[1]]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
if(vstd::contains(battleints,PlayerColor::UNFLAGGABLE))
battleints[PlayerColor::UNFLAGGABLE]->battleStart(info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], 1);
auto callBattleStart = [&](PlayerColor color, ui8 side){
if(vstd::contains(battleints, color))
battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side);
};
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide]))
callBattleStart(leftSide.color, 0);
callBattleStart(leftSide.color, 1);
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
{
boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide]]);
boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]);
}
}
void CClient::battleFinished()
{
for(PlayerColor side : gs->curB->sides)
if(battleCallbacks.count(side))
battleCallbacks[side]->setBattle(nullptr);
for(auto & side : gs->curB->sides)
if(battleCallbacks.count(side.color))
battleCallbacks[side.color]->setBattle(nullptr);
}
void CClient::loadNeutralBattleAI()

View File

@ -90,8 +90,8 @@
#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) \
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0], function, __VA_ARGS__) \
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1], function, __VA_ARGS__) \
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0].color, function, __VA_ARGS__) \
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1].color, function, __VA_ARGS__) \
BATTLE_INTERFACE_CALL_RECEIVERS(function, __VA_ARGS__)
/*
* NetPacksClient.cpp, part of VCMI engine
@ -624,7 +624,9 @@ void BattleSetActiveStack::applyCl( CClient *cl )
PlayerColor playerToCall; //player that will move activated stack
if( activated->hasBonusOfType(Bonus::HYPNOTIZED) )
{
playerToCall = ( GS(cl)->curB->sides[0] == activated->owner ? GS(cl)->curB->sides[1] : GS(cl)->curB->sides[0] );
playerToCall = ( GS(cl)->curB->sides[0].color == activated->owner
? GS(cl)->curB->sides[1].color
: GS(cl)->curB->sides[0].color );
}
else
{

View File

@ -101,7 +101,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
}
CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2,
CGHeroInstance *hero1, CGHeroInstance *hero2,
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
const SDL_Rect & myRect,
shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen)
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),

View File

@ -243,7 +243,7 @@ public:
ui32 animIDhelper; //for giving IDs for animations
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen); //c-tor
CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen); //c-tor
~CBattleInterface(); //d-tor
//std::vector<TimeInterested*> timeinterested; //animation handling

View File

@ -140,7 +140,7 @@ int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstan
CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const
{
int stackID = getIdForNewStack();
PlayerColor owner = attackerOwned ? sides[0] : sides[1];
PlayerColor owner = sides[attackerOwned ? 0 : 1].color;
assert((owner >= PlayerColor::PLAYER_LIMIT) ||
(base.armyObj && base.armyObj->tempOwner == owner));
@ -153,7 +153,7 @@ CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerO
CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const
{
int stackID = getIdForNewStack();
PlayerColor owner = attackerOwned ? sides[0] : sides[1];
PlayerColor owner = sides[attackerOwned ? 0 : 1].color;
auto ret = new CStack(&base, owner, stackID, attackerOwned, slot);
ret->position = position;
ret->state.insert(EBattleStackState::ALIVE); //alive state indication
@ -202,7 +202,7 @@ const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive)
) )
{
if (elem->alive())
return elem; //we prefer living stacks - there cna be only one stack on te tile, so return it imediately
return elem; //we prefer living stacks - there can be only one stack on the tile, so return it immediately
else if (!onlyAlive)
stack = elem; //dead stacks are only accessible when there's no alive stack on this tile
}
@ -212,15 +212,17 @@ const CStack * BattleInfo::battleGetStack(BattleHex pos, bool onlyAlive)
const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const
{
return heroes[!stack->attackerOwned];
return sides[!stack->attackerOwned].hero;
}
void BattleInfo::localInit()
{
belligerents[0]->battle = belligerents[1]->battle = this;
for(CArmedInstance *b : belligerents)
b->attachTo(this);
for(int i = 0; i < 2; i++)
{
auto armyObj = battleGetArmyObject(i);
armyObj->battle = this;
armyObj->attachTo(this);
}
for(CStack *s : stacks)
localInitStack(s);
@ -237,7 +239,7 @@ void BattleInfo::localInitStack(CStack * s)
}
else //attach directly to obj to which stack belongs and creature type
{
CArmedInstance *army = belligerents[!s->attackerOwned];
CArmedInstance *army = battleGetArmyObject(!s->attackerOwned);
s->attachTo(army);
assert(s->type);
s->attachTo(const_cast<CCreature*>(s->type));
@ -349,23 +351,17 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
{
CMP_stack cmpst;
auto curB = new BattleInfo;
curB->castSpells[0] = curB->castSpells[1] = 0;
curB->sides[0] = armies[0]->tempOwner;
curB->sides[1] = armies[1]->tempOwner;
if(curB->sides[1] == PlayerColor::UNFLAGGABLE)
curB->sides[1] = PlayerColor::NEUTRAL;
for(auto i = 0u; i < curB->sides.size(); i++)
curB->sides[i].init(heroes[i], armies[i]);
std::vector<CStack*> & stacks = (curB->stacks);
curB->tile = tile;
curB->battlefieldType = battlefieldType;
curB->belligerents[0] = const_cast<CArmedInstance*>(armies[0]);
curB->belligerents[1] = const_cast<CArmedInstance*>(armies[1]);
curB->heroes[0] = const_cast<CGHeroInstance*>(heroes[0]);
curB->heroes[1] = const_cast<CGHeroInstance*>(heroes[1]);
curB->round = -2;
curB->activeStack = -1;
curB->enchanterCounter[0] = curB->enchanterCounter[1] = 0; //ready to cast
if(town)
{
@ -664,7 +660,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
//////////////////////////////////////////////////////////////////////////
//tactics
bool isTacticsAllowed = !creatureBank; //no tactics in crebanks
bool isTacticsAllowed = !creatureBank; //no tactics in creature banks
int tacticLvls[2] = {0};
for(int i = 0; i < ARRAY_COUNT(tacticLvls); i++)
@ -687,7 +683,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
for(int i = 0; i < 2; i++)
{
TNodes nodes;
curB->belligerents[i]->getRedAncestors(nodes);
curB->battleGetArmyObject(i)->getRedAncestors(nodes);
for(CBonusSystemNode *n : nodes)
{
for(Bonus *b : n->getExportedBonusList())
@ -697,7 +693,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
auto bCopy = new Bonus(*b);
bCopy->effectRange = Bonus::NO_LIMIT;
bCopy->propagator.reset();
bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[!i]));
bCopy->limiter.reset(new StackOwnerLimiter(curB->sides[!i].color));
curB->addNewBonus(bCopy);
}
}
@ -709,11 +705,12 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
const CGHeroInstance * BattleInfo::getHero( PlayerColor player ) const
{
assert(sides[0] == player || sides[1] == player);
if(heroes[0] && heroes[0]->getOwner() == player)
return heroes[0];
for(int i = 0; i < sides.size(); i++)
if(sides[i].color == player)
return sides[i].hero;
return heroes[1];
logGlobal->errorStream() << "Player " << player << " is not in battle!";
return nullptr;
}
std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const
@ -767,13 +764,13 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C
PlayerColor BattleInfo::theOtherPlayer(PlayerColor player) const
{
return sides[!whatSide(player)];
return sides[!whatSide(player)].color;
}
ui8 BattleInfo::whatSide(PlayerColor player) const
{
for(int i = 0; i < ARRAY_COUNT(sides); i++)
if(sides[i] == player)
for(int i = 0; i < sides.size(); i++)
if(sides[i].color == player)
return i;
logGlobal->warnStream() << "BattleInfo::whatSide: Player " << player << " is not in battle!";
@ -841,6 +838,16 @@ BattleInfo::BattleInfo()
setNodeType(BATTLE);
}
CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const
{
return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side));
}
CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const
{
return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
}
CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
counterAttacks(1)
@ -1287,3 +1294,21 @@ CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn )
turn = Turn;
}
SideInBattle::SideInBattle()
{
hero = nullptr;
armyObject = nullptr;
castSpellsCount = 0;
enchanterCounter = 0;
}
void SideInBattle::init(const CGHeroInstance *Hero, const CArmedInstance *Army)
{
hero = Hero;
armyObject = Army;
color = armyObject->getOwner();
if(color == PlayerColor::UNFLAGGABLE)
color = PlayerColor::NEUTRAL;
}

View File

@ -41,22 +41,36 @@ struct DLL_LINKAGE SiegeInfo
}
};
struct DLL_LINKAGE SideInBattle
{
PlayerColor color;
const CGHeroInstance *hero; //may be NULL if army is not commanded by hero
const CArmedInstance *armyObject; //adv. map object with army that participates in battle; may be same as hero
ui8 castSpellsCount; //how many spells each side has cast this turn
std::vector<const CSpell *> usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill
si16 enchanterCounter; //tends to pass through 0, so sign is needed
SideInBattle();
void init(const CGHeroInstance *Hero, const CArmedInstance *Army);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & color & hero & armyObject;
h & castSpellsCount & usedSpellsHistory & enchanterCounter;
}
};
struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback
{
PlayerColor sides[2]; //sides[0] - attacker, sides[1] - defender
std::array<SideInBattle, 2> sides; //sides[0] - attacker, sides[1] - defender
si32 round, activeStack, selectedStack;
CGTownInstance::EFortLevel siege;
const CGTownInstance * town; //used during town siege - id of attacked town; -1 if not town defence
int3 tile; //for background and bonuses
CGHeroInstance* heroes[2];
CArmedInstance *belligerents[2]; //may be same as heroes
std::vector<CStack*> stacks;
std::vector<shared_ptr<CObstacleInstance> > obstacles;
ui8 castSpells[2]; //how many spells each side has cast this turn [0] - attacker, [1] - defender
std::vector<const CSpell *> usedSpellsHistory[2]; //each time hero casts spell, it's inserted here -> eagle eye skill
si16 enchanterCounter[2]; //tends to pass through 0, so sign is needed
SiegeInfo si;
BFieldType battlefieldType; //like !!BA:B
@ -67,10 +81,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
template <typename Handler> void serialize(Handler &h, const int version)
{
h & sides & round & activeStack & selectedStack & siege & town & tile & stacks & belligerents & obstacles
& castSpells & si & battlefieldType & terrainType;
h & heroes;
h & usedSpellsHistory & enchanterCounter;
h & sides;
h & round & activeStack & selectedStack & siege & town & tile & stacks & obstacles
& si & battlefieldType & terrainType;
h & tacticsSide & tacticDistance;
h & static_cast<CBonusSystemNode&>(*this);
}
@ -82,6 +95,10 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
//////////////////////////////////////////////////////////////////////////
CStack * getStackT(BattleHex tileID, bool onlyAlive = true);
CStack * getStack(int stackID, bool onlyAlive = true);
using CBattleInfoEssentials::battleGetArmyObject;
CArmedInstance * battleGetArmyObject(ui8 side) const;
using CBattleInfoEssentials::battleGetFightingHero;
CGHeroInstance * battleGetFightingHero(ui8 side) const;
const CStack * getNextStack() const; //which stack will have turn after current one
//void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
@ -170,7 +187,7 @@ public:
bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines
ui32 Speed(int turn = 0, bool useBind = false) const; //get speed of creature with all modificators
ui32 level() const;
si32 magicResistance() const; //include aura of resistance
si32 magicResistance() const override; //include aura of resistance
static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise

View File

@ -221,9 +221,9 @@ BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() co
RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
if(!player)
return BattlePerspective::ALL_KNOWING;
if(*player == getBattle()->sides[0])
if(*player == getBattle()->sides[0].color)
return BattlePerspective::LEFT_SIDE;
if(*player == getBattle()->sides[1])
if(*player == getBattle()->sides[1].color)
return BattlePerspective::RIGHT_SIDE;
logGlobal->errorStream() << "Cannot find player " << *player << " in battle!";
@ -281,12 +281,30 @@ const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) co
return nullptr;
}
return getBattle()->heroes[side];
return getBattle()->sides[side].hero;
}
const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) const
{
RETURN_IF_NOT_BATTLE(nullptr);
if(side > 1)
{
logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " wrong argument!";
return nullptr;
}
if(!battleDoWeKnowAbout(side))
{
logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " access check ";
return nullptr;
}
return getBattle()->sides[side].armyObject;
}
InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const
{
auto hero = getBattle()->heroes[side];
auto hero = getBattle()->sides[side].hero;
if(!hero)
{
logGlobal->warnStream() << __FUNCTION__ << ": side " << (int)side << " does not have hero!";
@ -299,7 +317,7 @@ InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const
int CBattleInfoEssentials::battleCastSpells(ui8 side) const
{
RETURN_IF_NOT_BATTLE(-1);
return getBattle()->castSpells[side];
return getBattle()->sides[side].castSpellsCount;
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(PlayerColor player, ECastingMode::ECastingMode mode) const
@ -366,7 +384,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
ui8 CBattleInfoEssentials::playerToSide(PlayerColor player) const
{
RETURN_IF_NOT_BATTLE(-1);
int ret = vstd::find_pos(getBattle()->sides, player);
int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; });
if(ret < 0)
logGlobal->warnStream() << "Cannot find side for player " << player;
@ -390,7 +408,7 @@ bool CBattleInfoEssentials::battleHasHero(ui8 side) const
{
RETURN_IF_NOT_BATTLE(false);
assert(side < 2);
return getBattle()->heroes[side];
return getBattle()->sides[side].hero;
}
ui8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const

View File

@ -20,6 +20,7 @@ struct BattleInfo;
struct CObstacleInstance;
class IBonusBearer;
struct InfoAboutHero;
class CArmedInstance;
namespace boost
{class shared_mutex;}
@ -182,6 +183,7 @@ public:
bool battleHasHero(ui8 side) const;
int battleCastSpells(ui8 side) const; //how many spells has given side casted
const CGHeroInstance * battleGetFightingHero(ui8 side) const; //depracated for players callback, easy to get wrong
const CArmedInstance * battleGetArmyObject(ui8 side) const;
InfoAboutHero battleGetHeroInfo(ui8 side) const;
//helpers

View File

@ -1015,10 +1015,10 @@ DLL_LINKAGE void BattleStart::applyGs( CGameState *gs )
DLL_LINKAGE void BattleNextRound::applyGs( CGameState *gs )
{
gs->curB->castSpells[0] = gs->curB->castSpells[1] = 0;
for (int i = 0; i < 2; ++i)
{
vstd::amax(--gs->curB->enchanterCounter[i], 0);
gs->curB->sides[i].castSpellsCount = 0;
vstd::amax(--gs->curB->sides[i].enchanterCounter, 0);
}
gs->curB->round = round;
@ -1106,11 +1106,10 @@ void BattleResult::applyGs( CGameState *gs )
for (auto & elem : gs->curB->stacks)
delete elem;
CGHeroInstance *h;
for (int i = 0; i < 2; ++i)
for(int i = 0; i < 2; ++i)
{
h = gs->curB->heroes[i];
if (h)
if(auto h = gs->curB->battleGetFightingHero(i))
{
h->getBonusList().remove_if(Bonus::OneBattle); //remove any "until next battle" bonuses
if (h->commander && h->commander->alive)
@ -1123,17 +1122,18 @@ void BattleResult::applyGs( CGameState *gs )
}
}
if (VLC->modh->modules.STACK_EXP)
if(VLC->modh->modules.STACK_EXP)
{
if (exp[0]) //checking local array is easier than dereferencing this crap twice
gs->curB->belligerents[0]->giveStackExp(exp[0]);
if (exp[1])
gs->curB->belligerents[1]->giveStackExp(exp[1]);
for(int i = 0; i < 2; i++)
if(exp[i])
gs->curB->battleGetArmyObject(i)->giveStackExp(exp[i]);
CBonusSystemNode::treeHasChanged();
}
gs->curB->belligerents[0]->battle = gs->curB->belligerents[1]->battle = nullptr;
for(int i = 0; i < 2; i++)
gs->curB->battleGetArmyObject(i)->battle = nullptr;
gs->curB.dellNull();
}
@ -1246,7 +1246,7 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs )
}
else
{
gs->curB->usedSpellsHistory[ba.side].push_back(SpellID(ba.additionalInfo).toSpell());
gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.additionalInfo).toSpell());
}
switch(ba.actionType)
@ -1273,8 +1273,8 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs )
assert(gs->curB);
if (castedByHero)
{
CGHeroInstance * h = gs->curB->heroes[side];
CGHeroInstance * enemy = gs->curB->heroes[1-side];
CGHeroInstance * h = gs->curB->battleGetFightingHero(side);
CGHeroInstance * enemy = gs->curB->battleGetFightingHero(!side);
h->mana -= spellCost;
vstd::amax(h->mana, 0);
@ -1282,7 +1282,7 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs )
enemy->mana += manaGained;
if (side < 2)
{
gs->curB->castSpells[side]++;
gs->curB->sides[side].castSpellsCount++;
}
}
@ -1532,12 +1532,12 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)
}
case ENCHANTER_COUNTER:
{
int side = gs->curB->whatSide(stack->owner);
auto & counter = gs->curB->sides[gs->curB->whatSide(stack->owner)].enchanterCounter;
if (absolute)
gs->curB->enchanterCounter[side] = val;
counter = val;
else
gs->curB->enchanterCounter[side] += val;
vstd::amax(gs->curB->enchanterCounter[side], 0);
counter += val;
vstd::amax(counter, 0);
break;
}
case UNBIND:

View File

@ -428,8 +428,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
if (hero2)
battleResult.data->exp[1] = hero2->calculateXp(battleResult.data->exp[1]);
const CArmedInstance *bEndArmy1 = gs->curB->belligerents[0];
const CArmedInstance *bEndArmy2 = gs->curB->belligerents[1];
const CArmedInstance *bEndArmy1 = gs->curB->sides[0].armyObject;
const CArmedInstance *bEndArmy2 = gs->curB->sides[1].armyObject;
const BattleResult::EResult result = battleResult.get()->result;
auto findBattleQuery = [this] () -> shared_ptr<CBattleQuery>
@ -452,8 +452,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
battleQuery = make_shared<CBattleQuery>(gs->curB);
}
}
if(battleQuery != queries.topQuery(gs->curB->sides[0]))
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0]) + " although in battle has no battle query at the top!");
if(battleQuery != queries.topQuery(gs->curB->sides[0].color))
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!");
battleQuery->result = *battleResult.data;
@ -480,7 +480,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
{
int maxLevel = eagleEyeLevel + 1;
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE);
for(const CSpell *sp : gs->curB->usedSpellsHistory[!battleResult.data->winner])
for(const CSpell *sp : gs->curB->sides[!battleResult.data->winner].usedSpellsHistory)
if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && rand() % 100 < eagleEyeChance)
cs.spells.insert(sp->id);
}
@ -534,7 +534,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
}
}
}
for (auto armySlot : gs->curB->belligerents[!battleResult.data->winner]->stacks)
for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
{
auto artifactsWorn = armySlot.second->artifactsWorn;
for (auto artSlot : artifactsWorn)
@ -647,7 +647,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
logGlobal->traceStream() << "Decremented queries count to " << finishingBattle->remainingBattleQueriesCount;
if(finishingBattle->remainingBattleQueriesCount > 0)
//Battle results will be hndled when all battle queries are closed
//Battle results will be handled when all battle queries are closed
return;
//TODO consider if we really want it to work like above. ATM each player as unblocked as soon as possible
@ -657,7 +657,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
// Necromancy if applicable.
const CStackBasicDescriptor raisedStack = finishingBattle->winnerHero ? finishingBattle->winnerHero->calculateNecromancy(*battleResult.data) : CStackBasicDescriptor();
// Give raised units to winner and show dialog, if any were raised,
// units will be given after casualities are taken
// units will be given after casualties are taken
const SlotID necroSlot = raisedStack.type ? finishingBattle->winnerHero->getSlotFor(raisedStack.type) : SlotID();
if (necroSlot != SlotID())
@ -706,12 +706,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
{
bat.bsa.clear();
bat.stackAttacking = att->ID;
int attackerLuck = att->LuckVal();
const CGHeroInstance * h0 = gs->curB->heroes[0],
* h1 = gs->curB->heroes[1];
const int attackerLuck = att->LuckVal();
auto sideHeroBlocksLuck = [](const SideInBattle &side){ return NBonus::hasOfType(side.hero, Bonus::BLOCK_LUCK); };
if(!(h0 && NBonus::hasOfType(h0, Bonus::BLOCK_LUCK)) &&
!(h1 && NBonus::hasOfType(h1, Bonus::BLOCK_LUCK)))
if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck))
{
if(attackerLuck > 0 && rand()%24 < attackerLuck)
{
@ -3339,7 +3338,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
}
case Battle::RETREAT: //retreat/flee
{
if(!gs->curB->battleCanFlee(gs->curB->sides[ba.side]))
if(!gs->curB->battleCanFlee(gs->curB->sides[ba.side].color))
complain("Cannot retreat!");
else
setBattleResult(BattleResult::ESCAPE, !ba.side); //surrendering side loses
@ -3347,7 +3346,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
}
case Battle::SURRENDER:
{
PlayerColor player = gs->curB->sides[ba.side];
PlayerColor player = gs->curB->sides[ba.side].color;
int cost = gs->curB->battleGetSurrenderCost(player);
if(cost < 0)
complain("Cannot surrender!");
@ -3477,7 +3476,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
//second shot for ballista, only if hero has advanced artillery
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
if( destStack->alive()
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
@ -3516,7 +3515,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{
StartAction start_action(ba);
sendAndApply(&start_action);
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS)];
EWallParts::EWallParts attackedPart = gs->curB->battleHexToWallPart(ba.destinationTile);
@ -3620,7 +3619,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{
StartAction start_action(ba);
sendAndApply(&start_action);
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
const CStack *healer = gs->curB->battleGetStackByID(ba.stackNumber),
*destStack = gs->curB->battleGetStackByPos(ba.destinationTile);
@ -4385,7 +4384,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
std::vector<CStack *> & battleStacks = gs->curB->stacks;
for (auto & battleStack : battleStacks)
{
if(battleStack->owner == gs->curB->sides[casterSide]) //get enemy stacks which can be affected by this spell
if(battleStack->owner == gs->curB->sides[casterSide].color) //get enemy stacks which can be affected by this spell
{
if (!gs->curB->battleIsImmune(nullptr, spell, ECastingMode::MAGIC_MIRROR, battleStack->position))
mirrorTargets.push_back(battleStack);
@ -4410,8 +4409,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
COMPLAIN_RET_FALSE_IF(ba.side > 1, "Side must be 0 or 1!");
const CGHeroInstance *h = gs->curB->heroes[ba.side];
const CGHeroInstance *secondHero = gs->curB->heroes[!ba.side];
const CGHeroInstance *h = gs->curB->battleGetFightingHero(ba.side);
const CGHeroInstance *secondHero = gs->curB->battleGetFightingHero(!ba.side);
if(!h)
{
logGlobal->warnStream() << "Wrong caster!";
@ -4534,10 +4533,11 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
if (st->hasBonusOfType(Bonus::MANA_DRAIN) && !vstd::contains(st->state, EBattleStackState::DRAINED_MANA))
{
const CGHeroInstance * enemy = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
const CGHeroInstance * owner = gs->curB->getHero(st->owner);
if (enemy)
{
ui32 manaDrained = st->valOfBonuses(Bonus::MANA_DRAIN);
vstd::amin (manaDrained, gs->curB->heroes[0]->mana);
vstd::amin(manaDrained, gs->curB->battleGetFightingHero(0)->mana);
if (manaDrained)
{
bte.effect = Bonus::MANA_DRAIN;
@ -4569,7 +4569,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
}
BonusList bl = *(st->getBonuses(Selector::type(Bonus::ENCHANTER)));
int side = gs->curB->whatSide(st->owner);
if (bl.size() && st->casts && !gs->curB->enchanterCounter[side])
if (bl.size() && st->casts && !gs->curB->sides[side].enchanterCounter)
{
int index = rand() % bl.size();
SpellID spellID = SpellID(bl[index]->subtype);
@ -4624,7 +4624,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c
//helper info
const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params
const ui8 side = curStack->attackerOwned; //if enemy is defending (false = 0), side of our hero is also 0 (attacker)
const CGHeroInstance *hero = gs->curB->heroes[side];
const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);
if(obstacle.obstacleType == CObstacleInstance::MOAT)
{
@ -5819,14 +5819,15 @@ void CGameHandler::runBattle()
}
//spells opening battle
for(int i=0; i<ARRAY_COUNT(gs->curB->heroes); ++i)
for(int i = 0; i < 2; ++i)
{
if(gs->curB->heroes[i] && gs->curB->heroes[i]->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL))
auto h = gs->curB->battleGetFightingHero(i);
if(h && h->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL))
{
TBonusListPtr bl = gs->curB->heroes[i]->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL));
TBonusListPtr bl = h->getBonuses(Selector::type(Bonus::OPENING_BATTLE_SPELL));
for (Bonus *b : *bl)
{
handleSpellCasting(SpellID(b->subtype), 3, -1, 0, gs->curB->heroes[i]->tempOwner, nullptr, gs->curB->heroes[1-i], b->val, ECastingMode::HERO_CASTING, nullptr);
handleSpellCasting(SpellID(b->subtype), 3, -1, 0, h->tempOwner, nullptr, gs->curB->battleGetFightingHero(1-i), b->val, ECastingMode::HERO_CASTING, nullptr);
}
}
}
@ -5853,7 +5854,8 @@ void CGameHandler::runBattle()
//check for bad morale => freeze
int nextStackMorale = next->MoraleVal();
if( nextStackMorale < 0 &&
!(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
!(NBonus::hasOfType(gs->curB->battleGetFightingHero(0), Bonus::BLOCK_MORALE)
|| NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
)
{
if( rand()%24 < -2 * nextStackMorale)
@ -6005,7 +6007,8 @@ void CGameHandler::runBattle()
&& !vstd::contains(next->state, EBattleStackState::FEAR)
&& next->alive()
&& nextStackMorale > 0
&& !(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses
&& !(NBonus::hasOfType(gs->curB->battleGetFightingHero(0), Bonus::BLOCK_MORALE)
|| NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses
)
{
if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn
@ -6033,7 +6036,7 @@ void CGameHandler::runBattle()
}
}
endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
endBattle(gs->curB->tile, gs->curB->battleGetFightingHero(0), gs->curB->battleGetFightingHero(1));
}
bool CGameHandler::makeAutomaticAction(const CStack *stack, BattleAction &ba)
@ -6210,7 +6213,7 @@ bool CGameHandler::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, con
void CGameHandler::duelFinished()
{
auto si = getStartInfo();
auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides[i]).name; };
auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides[i].color).name; };
int casualtiesPoints = 0;
logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:")
@ -6310,10 +6313,10 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper(shared_ptr<const CBat
auto &result = *Query->result;
auto &info = *Query->bi;
winnerHero = result.winner != 0 ? info.heroes[1] : info.heroes[0];
loserHero = result.winner != 0 ? info.heroes[0] : info.heroes[1];
victor = info.sides[result.winner];
loser = info.sides[!result.winner];
winnerHero = result.winner != 0 ? info.sides[1].hero : info.sides[0].hero;
loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero;
victor = info.sides[result.winner].color;
loser = info.sides[!result.winner].color;
duel = Duel;
remainingBattleQueriesCount = RemainingBattleQueriesCount;
}

View File

@ -222,13 +222,13 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit
CBattleQuery::CBattleQuery(const BattleInfo *Bi)
{
belligerents[0] = Bi->belligerents[0];
belligerents[1] = Bi->belligerents[1];
belligerents[0] = Bi->sides[0].armyObject;
belligerents[1] = Bi->sides[1].armyObject;
bi = Bi;
for(PlayerColor side : bi->sides)
addPlayer(side);
for(auto &side : bi->sides)
addPlayer(side.color);
}
CBattleQuery::CBattleQuery()

View File

@ -249,7 +249,7 @@ bool MakeAction::applyGh( CGameHandler *gh )
if(ba.actionType != Battle::WALK && ba.actionType != Battle::END_TACTIC_PHASE
&& ba.actionType != Battle::RETREAT && ba.actionType != Battle::SURRENDER)
ERROR_AND_RETURN;
if(gh->connections[b->sides[b->tacticsSide]] != c)
if(gh->connections[b->sides[b->tacticsSide].color] != c)
ERROR_AND_RETURN;
}
else if(gh->connections[b->battleGetStackByID(b->activeStack)->owner] != c)