mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Stupid AI is capable of winning / losing battle.
This commit is contained in:
parent
957f1764d7
commit
a53ec23556
@ -75,7 +75,7 @@ void CBattleLogic::SetCurrentTurn(int turn)
|
||||
void CBattleLogic::MakeStatistics(int currentCreatureId)
|
||||
{
|
||||
typedef std::vector<const CStack*> vector_stacks;
|
||||
vector_stacks allStacks = m_cb->battleGetStacks();
|
||||
vector_stacks allStacks = m_cb->battleGetStacks(false);
|
||||
const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
|
||||
if(currentStack->position < 0) //turret
|
||||
{
|
||||
|
@ -2,6 +2,13 @@
|
||||
#include "StupidAI.h"
|
||||
#include "../../lib/BattleState.h"
|
||||
#include "../../CCallback.h"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include <algorithm>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
IBattleCallback * cbc;
|
||||
|
||||
CStupidAI::CStupidAI(void)
|
||||
: side(-1), cb(NULL)
|
||||
@ -18,7 +25,7 @@ CStupidAI::~CStupidAI(void)
|
||||
void CStupidAI::init( IBattleCallback * CB )
|
||||
{
|
||||
print("init called, saving ptr to IBattleCallback");
|
||||
cb = CB;
|
||||
cbc = cb = CB;
|
||||
}
|
||||
|
||||
void CStupidAI::actionFinished( const BattleAction *action )
|
||||
@ -31,39 +38,121 @@ void CStupidAI::actionStarted( const BattleAction *action )
|
||||
print("actionStarted called");
|
||||
}
|
||||
|
||||
struct EnemyInfo
|
||||
{
|
||||
const CStack * s;
|
||||
int adi, adr;
|
||||
std::vector<THex> attackFrom; //for melee fight
|
||||
EnemyInfo(const CStack * _s) : s(_s)
|
||||
{}
|
||||
void calcDmg(const CStack * ourStack)
|
||||
{
|
||||
TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
|
||||
adi = (dmg.first + dmg.second) / 2;
|
||||
adr = (retal.first + retal.second) / 2;
|
||||
}
|
||||
|
||||
bool operator==(const EnemyInfo& ei) const
|
||||
{
|
||||
return s == ei.s;
|
||||
}
|
||||
};
|
||||
|
||||
bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
|
||||
{
|
||||
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
|
||||
}
|
||||
|
||||
int distToNearestNeighbour(THex hex, const std::vector<int> & dists, THex *chosenHex = NULL)
|
||||
{
|
||||
int ret = 1000000;
|
||||
BOOST_FOREACH(THex n, hex.neighbouringTiles())
|
||||
{
|
||||
if(dists[n] >= 0 && dists[n] < ret)
|
||||
{
|
||||
ret = dists[n];
|
||||
if(chosenHex)
|
||||
*chosenHex = n;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const std::vector<int> & dists)
|
||||
{
|
||||
return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
|
||||
}
|
||||
|
||||
static bool willSecondHexBlockMoreEnemyShooters(const THex &h1, const THex &h2)
|
||||
{
|
||||
int shooters[2] = {0}; //count of shooters on hexes
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
BOOST_FOREACH(THex neighbour, (i ? h2 : h1).neighbouringTiles())
|
||||
if(const CStack *s = cbc->battleGetStackByPos(neighbour))
|
||||
if(s->getCreature()->isShooting())
|
||||
shooters[i]++;
|
||||
|
||||
return shooters[0] < shooters[1];
|
||||
}
|
||||
|
||||
BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
{
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(2));
|
||||
print("activeStack called");
|
||||
std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
|
||||
std::vector<const CStack *> avEnemies;
|
||||
for(int g=0; g<avHexes.size(); ++g)
|
||||
std::vector<int> dists = cb->battleGetDistances(stack);
|
||||
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
|
||||
|
||||
BOOST_FOREACH(const CStack *s, cb->battleGetStacks())
|
||||
{
|
||||
const CStack * enemy = cb->battleGetStackByPos(avHexes[g]);
|
||||
if (enemy)
|
||||
if(s->owner != stack->owner)
|
||||
{
|
||||
avEnemies.push_back(enemy);
|
||||
if(cb->battleCanShoot(stack, s->position))
|
||||
{
|
||||
enemiesShootable.push_back(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_FOREACH(THex hex, avHexes)
|
||||
{
|
||||
if(CStack::isMeleeAttackPossible(stack, s, hex))
|
||||
{
|
||||
std::vector<EnemyInfo>::iterator i = std::find(enemiesReachable.begin(), enemiesReachable.end(), s);
|
||||
if(i == enemiesReachable.end())
|
||||
{
|
||||
enemiesReachable.push_back(s);
|
||||
i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
|
||||
}
|
||||
|
||||
i->attackFrom.push_back(hex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(stack->position % 17 < 5) //move army little towards enemy
|
||||
{
|
||||
THex dest = stack->position + !side*2 - 1;
|
||||
print(stack->nodeName() + "will be moved to " + boost::lexical_cast<std::string>(dest));
|
||||
return BattleAction::makeMove(stack, dest);
|
||||
if(!vstd::contains(enemiesReachable, s))
|
||||
enemiesUnreachable.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(avEnemies.size())
|
||||
if(enemiesShootable.size())
|
||||
{
|
||||
const CStack * enemy = avEnemies[0];
|
||||
//shooting
|
||||
if (cb->battleCanShoot(stack, enemy->position))
|
||||
{
|
||||
return BattleAction::makeShotAttack(stack, enemy);
|
||||
const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
|
||||
return BattleAction::makeShotAttack(stack, ei.s);
|
||||
}
|
||||
else if(enemiesReachable.size())
|
||||
{
|
||||
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
|
||||
return BattleAction::makeMeleeAttack(stack, ei.s, *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), &willSecondHexBlockMoreEnemyShooters));
|
||||
}
|
||||
else
|
||||
{
|
||||
const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), boost::bind(isCloser, _1, _2, boost::ref(dists)));
|
||||
if(distToNearestNeighbour(ei.s->position, dists) < BFIELD_SIZE)
|
||||
{
|
||||
return goTowards(stack, ei.s->position);
|
||||
}
|
||||
|
||||
//melee
|
||||
return BattleAction::makeMeleeAttack(stack, enemy, avHexes);
|
||||
}
|
||||
|
||||
return BattleAction::makeDefend(stack);
|
||||
@ -149,3 +238,28 @@ void CStupidAI::print(const std::string &text) const
|
||||
{
|
||||
tlog0 << "CStupidAI [" << this <<"]: " << text << std::endl;
|
||||
}
|
||||
|
||||
BattleAction CStupidAI::goTowards(const CStack * stack, THex hex)
|
||||
{
|
||||
THex realDest = hex;
|
||||
int predecessors[BFIELD_SIZE];
|
||||
std::vector<int> dists = cb->battleGetDistances(stack, hex);
|
||||
if(distToNearestNeighbour(hex, dists, &realDest) > BFIELD_SIZE)
|
||||
{
|
||||
print("goTowards: Cannot reach");
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
dists = cb->battleGetDistances(stack, realDest, predecessors);
|
||||
std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
|
||||
|
||||
while(1)
|
||||
{
|
||||
assert(realDest.isValid());
|
||||
if(vstd::contains(avHexes, hex))
|
||||
return BattleAction::makeMove(stack, hex);
|
||||
|
||||
hex = predecessors[hex];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,5 +31,6 @@ public:
|
||||
void battleCatapultAttacked(const CatapultAttack & ca) OVERRIDE; //called when catapult makes an attack
|
||||
void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
|
||||
|
||||
BattleAction goTowards(const CStack * stack, THex hex );
|
||||
};
|
||||
|
||||
|
@ -539,7 +539,7 @@ int CBattleCallback::battleGetPos(int stack)
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<const CStack*> CBattleCallback::battleGetStacks()
|
||||
std::vector<const CStack*> CBattleCallback::battleGetStacks(bool onlyAlive /*= true*/)
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
||||
std::vector<const CStack*> ret;
|
||||
@ -550,6 +550,7 @@ std::vector<const CStack*> CBattleCallback::battleGetStacks()
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const CStack *s, gs->curB->stacks)
|
||||
if(s->alive() || !onlyAlive)
|
||||
ret.push_back(s);
|
||||
|
||||
return ret;
|
||||
@ -628,12 +629,13 @@ int CBattleCallback::battleGetWallUnderHex(int hex)
|
||||
return gs->curB->hexToWallPart(hex);
|
||||
}
|
||||
|
||||
std::pair<ui32, ui32> CBattleCallback::battleEstimateDamage(int attackerID, int defenderID)
|
||||
TDmgRange CBattleCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg)
|
||||
{
|
||||
if(!gs->curB)
|
||||
return std::make_pair(0, 0);
|
||||
|
||||
const CGHeroInstance * attackerHero, * defenderHero;
|
||||
bool shooting = battleCanShoot(attacker, defender->position);
|
||||
|
||||
if(gs->curB->side1 == player)
|
||||
{
|
||||
@ -646,10 +648,27 @@ std::pair<ui32, ui32> CBattleCallback::battleEstimateDamage(int attackerID, int
|
||||
defenderHero = gs->curB->heroes[0];
|
||||
}
|
||||
|
||||
const CStack * attacker = gs->curB->getStack(attackerID, false),
|
||||
* defender = gs->curB->getStack(defenderID);
|
||||
TDmgRange ret = gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, shooting, 0, false);
|
||||
|
||||
return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker, defender->position), 0, false);
|
||||
if(retaliationDmg)
|
||||
{
|
||||
if(shooting)
|
||||
{
|
||||
retaliationDmg->first = retaliationDmg->second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui32 TDmgRange::* pairElems[] = {&TDmgRange::first, &TDmgRange::second};
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.damageAmount = ret.*pairElems[i];
|
||||
retaliationDmg->*pairElems[!i] = gs->curB->calculateDmgRange(defender, attacker, bsa.newAmount, attacker->count, attackerHero, defenderHero, false, false, false).*pairElems[!i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui8 CBattleCallback::battleGetSiegeLevel()
|
||||
@ -1040,3 +1059,30 @@ CBattleCallback::CBattleCallback(CGameState *GS, int Player, CClient *C )
|
||||
player = Player;
|
||||
cl = C;
|
||||
}
|
||||
|
||||
std::vector<int> CBattleCallback::battleGetDistances(const CStack * stack, THex hex /*= THex::INVALID*/, int * predecessors /*= NULL*/)
|
||||
{
|
||||
if(!hex.isValid())
|
||||
hex = stack->position;
|
||||
|
||||
std::vector<int> ret;
|
||||
bool ac[BFIELD_SIZE];
|
||||
int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
|
||||
gs->curB->makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
|
||||
|
||||
for(int i=0; i<BFIELD_SIZE; ++i)
|
||||
{
|
||||
if(pr[i] == -1)
|
||||
ret.push_back(-1);
|
||||
else
|
||||
ret.push_back(dist[i]);
|
||||
}
|
||||
|
||||
if(predecessors)
|
||||
{
|
||||
memcpy(predecessors, pr, BFIELD_SIZE * sizeof(int));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
10
CCallback.h
10
CCallback.h
@ -82,16 +82,17 @@ public:
|
||||
virtual const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true)=0; //returns stack info by given pos
|
||||
virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack
|
||||
virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
|
||||
virtual std::vector<const CStack*> battleGetStacks()=0; //returns stacks on battlefield
|
||||
virtual std::vector<const CStack*> battleGetStacks(bool onlyAlive = true)=0; //returns stacks on battlefield
|
||||
virtual void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
|
||||
virtual std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
|
||||
virtual std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, int * predecessors = NULL)=0; //returns vector of distances to [dest hex number]
|
||||
virtual bool battleCanShoot(const CStack * stack, THex dest)=0; //returns true if unit with id ID can shoot to dest
|
||||
virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
|
||||
virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
|
||||
virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
|
||||
virtual ui8 battleGetWallState(int partOfWall)=0; //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
|
||||
virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
|
||||
virtual std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
|
||||
virtual TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
|
||||
virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
|
||||
virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender)
|
||||
virtual si8 battleHasDistancePenalty(const CStack * stack, THex destHex) =0; //checks if given stack has distance penalty
|
||||
@ -209,16 +210,17 @@ public:
|
||||
const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true) OVERRIDE; //returns stack info by given pos
|
||||
int battleGetPos(int stack) OVERRIDE; //returns position (tile ID) of stack
|
||||
int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
|
||||
std::vector<const CStack*> battleGetStacks() OVERRIDE; //returns stacks on battlefield
|
||||
std::vector<const CStack*> battleGetStacks(bool onlyAlive = true) OVERRIDE; //returns stacks on battlefield
|
||||
void getStackQueue( std::vector<const CStack *> &out, int howMany ) OVERRIDE; //returns vector of stack in order of their move sequence
|
||||
std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable) OVERRIDE; //reutrns numbers of hexes reachable by creature with id ID
|
||||
std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, int * predecessors = NULL) OVERRIDE; //returns vector of distances to [dest hex number]; if predecessors is not null, it must point to BFIELD_SIZE * sizeof(int) of allocated memory
|
||||
bool battleCanShoot(const CStack * stack, THex dest) OVERRIDE; //returns true if unit with id ID can shoot to dest
|
||||
bool battleCanCastSpell() OVERRIDE; //returns true, if caller can cast a spell
|
||||
bool battleCanFlee() OVERRIDE; //returns true if caller can flee from the battle
|
||||
const CGTownInstance * battleGetDefendedTown() OVERRIDE; //returns defended town if current battle is a siege, NULL instead
|
||||
ui8 battleGetWallState(int partOfWall) OVERRIDE; //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
|
||||
int battleGetWallUnderHex(int hex) OVERRIDE; //returns part of destructible wall / gate / keep under given hex or -1 if not found
|
||||
std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID) OVERRIDE; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
|
||||
TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL) OVERRIDE; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
|
||||
ui8 battleGetSiegeLevel() OVERRIDE; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
|
||||
const CGHeroInstance * battleGetFightingHero(ui8 side) const OVERRIDE; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
|
||||
si8 battleHasDistancePenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has distance penalty
|
||||
|
@ -913,6 +913,10 @@ bool CMeleeAttack::init()
|
||||
static const int mutPosToGroup[] = {11, 11, 12, 13, 13, 12};
|
||||
|
||||
int mutPos = THex::mutualPosition(attackingStackPosBeforeReturn + reversedShift, dest);
|
||||
if(mutPos == -1 && attackedStack->doubleWide())
|
||||
{
|
||||
mutPos = THex::mutualPosition(attackingStackPosBeforeReturn + reversedShift, attackedStack->occupiedHex());
|
||||
}
|
||||
switch(mutPos) //attack direction
|
||||
{
|
||||
case 0: case 1: case 2: case 3: case 4: case 5:
|
||||
@ -920,6 +924,8 @@ bool CMeleeAttack::init()
|
||||
break;
|
||||
default:
|
||||
tlog1<<"Critical Error! Wrong dest in stackAttacking! dest: "<<dest<<" attacking stack pos: "<<attackingStackPosBeforeReturn<<" reversed shift: "<<reversedShift<<std::endl;
|
||||
group = 11;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1129,7 +1135,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
|
||||
//initializing armies
|
||||
this->army1 = army1;
|
||||
this->army2 = army2;
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks();
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false);
|
||||
BOOST_FOREACH(const CStack *s, stacks)
|
||||
{
|
||||
newStack(s);
|
||||
@ -1455,7 +1461,7 @@ void CBattleInterface::deactivate()
|
||||
|
||||
void CBattleInterface::show(SDL_Surface * to)
|
||||
{
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(); //used in a few places
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false); //used in a few places
|
||||
++animCount;
|
||||
if(!to) //"evaluating" to
|
||||
to = screen;
|
||||
@ -1806,7 +1812,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
//setting console text
|
||||
char buf[500];
|
||||
//calculating estimated dmg
|
||||
std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive->ID, shere->ID);
|
||||
std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
|
||||
std::ostringstream estDmg;
|
||||
estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
|
||||
//printing
|
||||
@ -1956,7 +1962,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
//setting console info
|
||||
char buf[500];
|
||||
//calculating estimated dmg
|
||||
std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive->ID, shere->ID);
|
||||
std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
|
||||
std::ostringstream estDmg;
|
||||
estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
|
||||
//printing
|
||||
@ -2273,7 +2279,7 @@ void CBattleInterface::stackAttacking( const CStack * attacker, THex dest, const
|
||||
void CBattleInterface::newRoundFirst( int round )
|
||||
{
|
||||
//handle regeneration
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks();
|
||||
std::vector<const CStack*> stacks = curInt->cb->battleGetStacks(false);
|
||||
BOOST_FOREACH(const CStack *s, stacks)
|
||||
{
|
||||
//don't show animation when no HP is regenerated
|
||||
|
19
global.h
19
global.h
@ -18,7 +18,7 @@ typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
|
||||
typedef boost::int8_t si8; //signed int 8 bits (1 byte)
|
||||
typedef si64 expType;
|
||||
typedef ui16 spelltype;
|
||||
|
||||
typedef std::pair<ui32, ui32> TDmgRange;
|
||||
|
||||
#include "int3.h"
|
||||
#include <map>
|
||||
@ -132,20 +132,26 @@ const int SPELLBOOK_GOLD_COST = 500;
|
||||
//for battle stacks' positions
|
||||
struct THex
|
||||
{
|
||||
static const si16 INVALID = -1;
|
||||
enum EDir{RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT};
|
||||
|
||||
si16 hex;
|
||||
|
||||
THex() : hex(-1) {}
|
||||
THex() : hex(INVALID) {}
|
||||
THex(si16 _hex) : hex(_hex)
|
||||
{
|
||||
assert(hex >= 0 && hex < BFIELD_SIZE);
|
||||
//assert(isValid());
|
||||
}
|
||||
operator si16() const
|
||||
{
|
||||
return hex;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return hex >= 0 && hex < BFIELD_SIZE;
|
||||
}
|
||||
|
||||
template<typename inttype>
|
||||
THex(inttype x, inttype y)
|
||||
{
|
||||
@ -208,18 +214,25 @@ struct THex
|
||||
{
|
||||
case TOP_LEFT:
|
||||
setXY(y%2 ? x-1 : x, y-1);
|
||||
break;
|
||||
case TOP_RIGHT:
|
||||
setXY(y%2 ? x : x+1, y-1);
|
||||
break;
|
||||
case RIGHT:
|
||||
setXY(x+1, y);
|
||||
break;
|
||||
case BOTTOM_RIGHT:
|
||||
setXY(y%2 ? x : x+1, y+1);
|
||||
break;
|
||||
case BOTTOM_LEFT:
|
||||
setXY(y%2 ? x-1 : x, y+1);
|
||||
break;
|
||||
case LEFT:
|
||||
setXY(x-1, y);
|
||||
break;
|
||||
default:
|
||||
throw std::string("Disaster: wrong direction in THex::operator+=!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,34 +30,18 @@ BattleAction BattleAction::makeDefend(const CStack *stack)
|
||||
return ba;
|
||||
}
|
||||
|
||||
BattleAction BattleAction::makeMeleeAttack( const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker )
|
||||
|
||||
BattleAction BattleAction::makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom /*= THex::INVALID*/)
|
||||
{
|
||||
BattleAction ba;
|
||||
ba.side = !stack->attackerOwned;
|
||||
ba.actionType = WALK_AND_ATTACK;
|
||||
ba.stackNumber = stack->ID;
|
||||
ba.destinationTile = -1;
|
||||
for (int g=0; g<reachableByAttacker.size(); ++g)
|
||||
{
|
||||
if (THex::mutualPosition(reachableByAttacker[g], attacked->position) >= 0 )
|
||||
{
|
||||
ba.destinationTile = reachableByAttacker[g];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ba.destinationTile == -1)
|
||||
{
|
||||
//we couldn't determine appropriate pos
|
||||
//TODO: should we throw an exception?
|
||||
return makeDefend(stack);
|
||||
}
|
||||
|
||||
ba.destinationTile = attackFrom;
|
||||
ba.additionalInfo = attacked->position;
|
||||
|
||||
return ba;
|
||||
}
|
||||
|
||||
}
|
||||
BattleAction BattleAction::makeWait(const CStack *stack)
|
||||
{
|
||||
BattleAction ba;
|
||||
|
@ -25,7 +25,7 @@ struct DLL_EXPORT BattleAction
|
||||
};
|
||||
ui8 actionType; //use ActionType enum for values
|
||||
//10 = Monster casts a spell (i.e. Faerie Dragons) 11 - Bad morale freeze 12 - stacks heals another stack
|
||||
ui16 destinationTile;
|
||||
THex destinationTile;
|
||||
si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -36,7 +36,7 @@ struct DLL_EXPORT BattleAction
|
||||
|
||||
static BattleAction makeDefend(const CStack *stack);
|
||||
static BattleAction makeWait(const CStack *stack);
|
||||
static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker);
|
||||
static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom = THex::INVALID);
|
||||
static BattleAction makeShotAttack(const CStack *shooter, const CStack *target);
|
||||
static BattleAction makeMove(const CStack *stack, THex dest);
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "CTownHandler.h"
|
||||
#include "NetPacks.h"
|
||||
|
||||
/*
|
||||
* BattleState.h, part of VCMI engine
|
||||
@ -326,7 +327,7 @@ std::vector<THex> BattleInfo::getAccessibility(const CStack * stack, bool addOcc
|
||||
|
||||
return ret;
|
||||
}
|
||||
bool BattleInfo::isStackBlocked(const CStack * stack)
|
||||
bool BattleInfo::isStackBlocked(const CStack * stack) const
|
||||
{
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
|
||||
return false;
|
||||
@ -377,11 +378,11 @@ std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool
|
||||
return std::make_pair(path, dist[dest]);
|
||||
}
|
||||
|
||||
std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
|
||||
TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky ) const
|
||||
{
|
||||
float additiveBonus=1.0f, multBonus=1.0f,
|
||||
minDmg = attacker->getMinDamage() * attacker->count,
|
||||
maxDmg = attacker->getMaxDamage() * attacker->count;
|
||||
minDmg = attacker->getMinDamage() * attackerCount,
|
||||
maxDmg = attacker->getMaxDamage() * attackerCount;
|
||||
|
||||
if(attacker->getCreature()->idNumber == 149) //arrow turret
|
||||
{
|
||||
@ -569,7 +570,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
|
||||
minDmg *= additiveBonus * multBonus;
|
||||
maxDmg *= additiveBonus * multBonus;
|
||||
|
||||
std::pair<ui32, ui32> returnedVal;
|
||||
TDmgRange returnedVal;
|
||||
|
||||
if(attacker->getEffect(42)) //curse handling (rest)
|
||||
{
|
||||
@ -593,9 +594,14 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
|
||||
return returnedVal;
|
||||
}
|
||||
|
||||
TDmgRange BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const
|
||||
{
|
||||
return calculateDmgRange(attacker, defender, attacker->count, defender->count, attackerHero, defendingHero, shooting, charge, lucky);
|
||||
}
|
||||
|
||||
ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
|
||||
{
|
||||
std::pair<ui32, ui32> range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
|
||||
TDmgRange range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
|
||||
|
||||
if(range.first != range.second)
|
||||
{
|
||||
@ -1021,7 +1027,7 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
|
||||
}
|
||||
}
|
||||
|
||||
si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex )
|
||||
si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex ) const
|
||||
{
|
||||
struct HLP
|
||||
{
|
||||
@ -1042,7 +1048,7 @@ si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex )
|
||||
|
||||
}
|
||||
|
||||
si8 BattleInfo::sameSideOfWall(int pos1, int pos2)
|
||||
si8 BattleInfo::sameSideOfWall(int pos1, int pos2) const
|
||||
{
|
||||
int wallInStackLine = lineToWallHex(pos1/BFIELD_WIDTH);
|
||||
int wallInDestLine = lineToWallHex(pos2/BFIELD_WIDTH);
|
||||
@ -1053,7 +1059,7 @@ si8 BattleInfo::sameSideOfWall(int pos1, int pos2)
|
||||
return stackLeft != destLeft;
|
||||
}
|
||||
|
||||
si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex )
|
||||
si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex ) const
|
||||
{
|
||||
if (siege == 0)
|
||||
{
|
||||
@ -1067,7 +1073,7 @@ si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex )
|
||||
return !sameSideOfWall(stack->position, destHex);
|
||||
}
|
||||
|
||||
si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel)
|
||||
si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const
|
||||
{
|
||||
bool ac[BFIELD_SIZE];
|
||||
|
||||
@ -1107,7 +1113,7 @@ si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLev
|
||||
// }
|
||||
// }
|
||||
|
||||
bool BattleInfo::battleCanShoot(const CStack * stack, THex dest)
|
||||
bool BattleInfo::battleCanShoot(const CStack * stack, THex dest) const
|
||||
{
|
||||
const CStack *dst = getStackT(dest);
|
||||
|
||||
@ -1131,7 +1137,7 @@ bool BattleInfo::battleCanShoot(const CStack * stack, THex dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BattleInfo::battleCanFlee(int player)
|
||||
bool BattleInfo::battleCanFlee(int player) const
|
||||
{
|
||||
if (player == side1)
|
||||
{
|
||||
@ -1171,12 +1177,12 @@ const CStack * BattleInfo::battleGetStack(THex pos, bool onlyAlive)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack)
|
||||
const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack) const
|
||||
{
|
||||
return heroes[!stack->attackerOwned];
|
||||
}
|
||||
|
||||
si8 BattleInfo::battleMaxSpellLevel()
|
||||
si8 BattleInfo::battleMaxSpellLevel() const
|
||||
{
|
||||
// if(!curB) //there is not battle
|
||||
// {
|
||||
@ -1944,6 +1950,55 @@ std::string CStack::nodeName() const
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void CStack::prepareAttacked(BattleStackAttacked &bsa) const
|
||||
{
|
||||
bsa.killedAmount = bsa.damageAmount / MaxHealth();
|
||||
unsigned damageFirst = bsa.damageAmount % MaxHealth();
|
||||
|
||||
if( firstHPleft <= damageFirst )
|
||||
{
|
||||
bsa.killedAmount++;
|
||||
bsa.newHP = firstHPleft + MaxHealth() - damageFirst;
|
||||
}
|
||||
else
|
||||
{
|
||||
bsa.newHP = firstHPleft - damageFirst;
|
||||
}
|
||||
|
||||
if(count <= bsa.killedAmount) //stack killed
|
||||
{
|
||||
bsa.newAmount = 0;
|
||||
bsa.flags |= 1;
|
||||
bsa.killedAmount = count; //we cannot kill more creatures than we have
|
||||
}
|
||||
else
|
||||
{
|
||||
bsa.newAmount = count - bsa.killedAmount;
|
||||
}
|
||||
}
|
||||
|
||||
bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos /*= THex::INVALID*/, THex defenderPos /*= THex::INVALID*/)
|
||||
{
|
||||
if (!attackerPos.isValid())
|
||||
{
|
||||
attackerPos = attacker->position;
|
||||
}
|
||||
if (!defenderPos.isValid())
|
||||
{
|
||||
defenderPos = defender->position;
|
||||
}
|
||||
|
||||
return
|
||||
(THex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front
|
||||
|| (attacker->doubleWide() //back <=> front
|
||||
&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0)
|
||||
|| (defender->doubleWide() //front <=> back
|
||||
&& THex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0)
|
||||
|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
|
||||
&& THex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0);
|
||||
|
||||
}
|
||||
|
||||
bool CMP_stack::operator()( const CStack* a, const CStack* b )
|
||||
{
|
||||
switch(phase)
|
||||
|
@ -21,7 +21,7 @@ class CStack;
|
||||
class CArmedInstance;
|
||||
class CGTownInstance;
|
||||
class CStackInstance;
|
||||
|
||||
struct BattleStackAttacked;
|
||||
|
||||
struct DLL_EXPORT CObstacleInstance
|
||||
{
|
||||
@ -84,10 +84,11 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
|
||||
std::pair< std::vector<int>, int > getPath(int start, int dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
|
||||
std::vector<THex> getAccessibility(const CStack * stack, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
|
||||
|
||||
bool isStackBlocked(const CStack * stack); //returns true if there is neighboring enemy stack
|
||||
bool isStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack
|
||||
|
||||
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting)
|
||||
std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
|
||||
TDmgRange calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
|
||||
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
|
||||
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
|
||||
std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, int destinationTile); //calculates stack affected by given spell
|
||||
static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
|
||||
@ -100,16 +101,16 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
|
||||
ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||
ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
|
||||
ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const;
|
||||
si8 hasDistancePenalty(const CStack * stackID, THex destHex); //determines if given stack has distance penalty shooting given pos
|
||||
si8 sameSideOfWall(int pos1, int pos2); //determines if given positions are on the same side of wall
|
||||
si8 hasWallPenalty(const CStack * stack, THex destHex); //determines if given stack has wall penalty shooting given pos
|
||||
si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel); //determines if given stack can teleport to given place
|
||||
bool battleCanShoot(const CStack * stack, THex dest); //determines if stack with given ID shoot at the selected destination
|
||||
si8 hasDistancePenalty(const CStack * stackID, THex destHex) const; //determines if given stack has distance penalty shooting given pos
|
||||
si8 sameSideOfWall(int pos1, int pos2) const; //determines if given positions are on the same side of wall
|
||||
si8 hasWallPenalty(const CStack * stack, THex destHex) const; //determines if given stack has wall penalty shooting given pos
|
||||
si8 canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const; //determines if given stack can teleport to given place
|
||||
bool battleCanShoot(const CStack * stack, THex dest) const; //determines if stack with given ID shoot at the selected destination
|
||||
|
||||
bool battleCanFlee(int player); //returns true if player can flee from the battle
|
||||
bool battleCanFlee(int player) const; //returns true if player can flee from the battle
|
||||
const CStack * battleGetStack(THex pos, bool onlyAlive); //returns stack at given tile
|
||||
const CGHeroInstance * battleGetOwner(const CStack * stack); //returns hero that owns given stack; NULL if none
|
||||
si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
|
||||
const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
|
||||
si8 battleMaxSpellLevel() const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
|
||||
void localInit();
|
||||
static BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
|
||||
};
|
||||
@ -167,9 +168,13 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos = THex::INVALID, THex defenderPos = THex::INVALID);
|
||||
|
||||
bool doubleWide() const;
|
||||
int occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
|
||||
|
||||
void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
assert(isIndependentNode());
|
||||
|
@ -905,11 +905,12 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
|
||||
h->subID = 1;
|
||||
h->initHero(1);
|
||||
h->initObj();
|
||||
//h->putStack(0, new CStackInstance(34, 5));
|
||||
h->setCreature(0, 110, 1);
|
||||
|
||||
CGCreature *c = new CGCreature();
|
||||
c->setOwner(1);
|
||||
c->putStack(0, new CStackInstance(70, 6));
|
||||
c->putStack(0, new CStackInstance(69, 6));
|
||||
c->putStack(1, new CStackInstance(11, 3));
|
||||
c->subID = 34;
|
||||
c->initObj();
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../client/CSoundBase.h"
|
||||
#include "CGameHandler.h"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
/*
|
||||
* CGameHandler.cpp, part of VCMI engine
|
||||
@ -309,8 +309,6 @@ void CGameHandler::changeSecSkill( int ID, int which, int val, bool abs/*=false*
|
||||
void CGameHandler::startBattle( const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town /*= NULL*/ )
|
||||
{
|
||||
battleEndCallback = new boost::function<void(BattleResult*)>(cb);
|
||||
bEndArmy1 = armies[0];
|
||||
bEndArmy2 = armies[1];
|
||||
{
|
||||
setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
||||
}
|
||||
@ -321,6 +319,9 @@ void CGameHandler::startBattle( const CArmedInstance *armies[2], int3 tile, cons
|
||||
void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
|
||||
{
|
||||
BattleResultsApplied resultsApplied;
|
||||
|
||||
const CArmedInstance *bEndArmy1 = gs->curB->belligerents[0];
|
||||
const CArmedInstance *bEndArmy2 = gs->curB->belligerents[0];
|
||||
resultsApplied.player1 = bEndArmy1->tempOwner;
|
||||
resultsApplied.player2 = bEndArmy2->tempOwner;
|
||||
|
||||
@ -425,33 +426,6 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
|
||||
delete battleResult.data;
|
||||
}
|
||||
|
||||
void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, const CStack *def)
|
||||
{
|
||||
bsa.killedAmount = bsa.damageAmount / def->MaxHealth();
|
||||
unsigned damageFirst = bsa.damageAmount % def->MaxHealth();
|
||||
|
||||
if( def->firstHPleft <= damageFirst )
|
||||
{
|
||||
bsa.killedAmount++;
|
||||
bsa.newHP = def->firstHPleft + def->MaxHealth() - damageFirst;
|
||||
}
|
||||
else
|
||||
{
|
||||
bsa.newHP = def->firstHPleft - damageFirst;
|
||||
}
|
||||
|
||||
if(def->count <= bsa.killedAmount) //stack killed
|
||||
{
|
||||
bsa.newAmount = 0;
|
||||
bsa.flags |= 1;
|
||||
bsa.killedAmount = def->count; //we cannot kill more creatures than we have
|
||||
}
|
||||
else
|
||||
{
|
||||
bsa.newAmount = def->count - bsa.killedAmount;
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance)
|
||||
{
|
||||
bat.bsa.clear();
|
||||
@ -480,7 +454,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
||||
|
||||
|
||||
int dmg = bsa->damageAmount;
|
||||
prepareAttacked(*bsa, def);
|
||||
def->prepareAttacked(*bsa);
|
||||
|
||||
//life drain handling
|
||||
if (att->hasBonusOfType(Bonus::LIFE_DRAIN))
|
||||
@ -515,7 +489,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
||||
bsa->effect = 11;
|
||||
|
||||
bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
|
||||
prepareAttacked(*bsa, att);
|
||||
att->prepareAttacked(*bsa);
|
||||
}
|
||||
|
||||
}
|
||||
@ -3026,21 +3000,21 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
bool ok = true;
|
||||
switch(ba.actionType)
|
||||
{
|
||||
case 2: //walk
|
||||
case BattleAction::WALK: //walk
|
||||
{
|
||||
sendAndApply(&StartAction(ba)); //start movement
|
||||
moveStack(ba.stackNumber,ba.destinationTile); //move
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
case 3: //defend
|
||||
case 8: //wait
|
||||
case BattleAction::DEFEND: //defend
|
||||
case BattleAction::WAIT: //wait
|
||||
{
|
||||
sendAndApply(&StartAction(ba));
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
case 4: //retreat/flee
|
||||
case BattleAction::RETREAT: //retreat/flee
|
||||
{
|
||||
if( !gs->curB->battleCanFlee(ba.side ? gs->curB->side2 : gs->curB->side1) )
|
||||
break;
|
||||
@ -3053,7 +3027,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
battleResult.set(br);
|
||||
break;
|
||||
}
|
||||
case 6: //walk or attack
|
||||
case BattleAction::WALK_AND_ATTACK: //walk or attack
|
||||
{
|
||||
sendAndApply(&StartAction(ba)); //start movement and attack
|
||||
int startingPos = gs->curB->getStack(ba.stackNumber)->position;
|
||||
@ -3075,37 +3049,20 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
break;
|
||||
}
|
||||
|
||||
if(curStack->ID == stackAtEnd->ID) //we should just move, it will be handled by following check
|
||||
if(stackAtEnd && curStack->ID == stackAtEnd->ID) //we should just move, it will be handled by following check
|
||||
{
|
||||
stackAtEnd = NULL;
|
||||
}
|
||||
|
||||
if(!stackAtEnd)
|
||||
{
|
||||
std::ostringstream problem;
|
||||
problem << "There is no stack on " << ba.additionalInfo << " tile (no attack)!";
|
||||
std::string probl = problem.str();
|
||||
tlog3 << probl << std::endl;
|
||||
complain(probl);
|
||||
complain(boost::str(boost::format("walk and attack error: no stack at additionalInfo tile (%d)!\n") % ba.additionalInfo));
|
||||
ok = false;
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
|
||||
ui16 curpos = curStack->position,
|
||||
enemypos = stackAtEnd->position;
|
||||
|
||||
|
||||
if( !(
|
||||
(THex::mutualPosition(curpos, enemypos) >= 0) //front <=> front
|
||||
|| (curStack->doubleWide() //back <=> front
|
||||
&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
|
||||
|| (stackAtEnd->doubleWide() //front <=> back
|
||||
&& THex::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
|
||||
|| (stackAtEnd->doubleWide() && curStack->doubleWide()//back <=> back
|
||||
&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
|
||||
)
|
||||
)
|
||||
if( !CStack::isMeleeAttackPossible(curStack, stackAtEnd) )
|
||||
{
|
||||
tlog3 << "Attack cannot be performed!";
|
||||
sendAndApply(&EndAction());
|
||||
@ -3152,7 +3109,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
case 7: //shoot
|
||||
case BattleAction::SHOOT: //shoot
|
||||
{
|
||||
CStack *curStack = gs->curB->getStack(ba.stackNumber),
|
||||
*destStack= gs->curB->getStackT(ba.destinationTile);
|
||||
@ -3180,7 +3137,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
case 9: //catapult
|
||||
case BattleAction::CATAPULT: //catapult
|
||||
{
|
||||
sendAndApply(&StartAction(ba));
|
||||
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
|
||||
@ -3282,7 +3239,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
sendAndApply(&EndAction());
|
||||
break;
|
||||
}
|
||||
case 12: //healing
|
||||
case BattleAction::STACK_HEAL: //healing
|
||||
{
|
||||
sendAndApply(&StartAction(ba));
|
||||
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
|
||||
@ -3609,7 +3566,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
|
||||
bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower);
|
||||
bsa.stackAttacked = (*it)->ID;
|
||||
bsa.attackerID = -1;
|
||||
prepareAttacked(bsa,*it);
|
||||
(*it)->prepareAttacked(bsa);
|
||||
si.stacks.push_back(bsa);
|
||||
}
|
||||
if(!si.stacks.empty())
|
||||
|
@ -113,12 +113,11 @@ public:
|
||||
|
||||
////used only in endBattle - don't touch elsewhere
|
||||
boost::function<void(BattleResult*)> * battleEndCallback;
|
||||
const CArmedInstance * bEndArmy1, * bEndArmy2;
|
||||
//const CArmedInstance * bEndArmy1, * bEndArmy2;
|
||||
bool visitObjectAfterVictory;
|
||||
//
|
||||
void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
|
||||
void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
|
||||
void prepareAttacked(BattleStackAttacked &bsa, const CStack *def);
|
||||
void checkForBattleEnd( std::vector<CStack*> &stacks );
|
||||
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user