1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

* some progress in StupidAI

* refactoring of battle handling
WARNING: strange crash on entering battle
This commit is contained in:
mateuszb 2011-01-07 10:48:31 +00:00
parent 401b364ad7
commit 957f1764d7
16 changed files with 457 additions and 305 deletions

View File

@ -485,7 +485,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
return BattleAction::makeDefend(attackerStack);
}
if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID))) // shoot
if (m_cb->battleCanShoot(attackerStack, destinationStack->position)) // shoot
{
return BattleAction::makeShotAttack(attackerStack, destinationStack);
}
@ -519,7 +519,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
}
}
std::vector<int> fields = m_cb->battleGetAvailableHexes(attackerID, false);
std::vector<THex> fields = m_cb->battleGetAvailableHexes(m_cb->battleGetStackByID(attackerID), false);
if(fields.size() == 0)
{
@ -533,11 +533,11 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
ba.destinationTile = static_cast<ui16>(dest_tile);
//simplified checking for possibility of attack (previous was too simplified)
int destStackPos = m_cb->battleGetPos(destinationID);
if(BattleInfo::mutualPosition(dest_tile, destStackPos) != -1)
if(THex::mutualPosition(dest_tile, destStackPos) != -1)
ba.additionalInfo = destStackPos;
else if(BattleInfo::mutualPosition(dest_tile, destStackPos+1) != -1)
else if(THex::mutualPosition(dest_tile, destStackPos+1) != -1)
ba.additionalInfo = destStackPos+1;
else if(BattleInfo::mutualPosition(dest_tile, destStackPos-1) != -1)
else if(THex::mutualPosition(dest_tile, destStackPos-1) != -1)
ba.additionalInfo = destStackPos-1;
else
return BattleAction::makeDefend(attackerStack);
@ -574,7 +574,7 @@ BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
}
}
for (std::vector<int>::const_iterator it = fields.begin(); it != fields.end(); ++it)
for (std::vector<THex>::const_iterator it = fields.begin(); it != fields.end(); ++it)
{
if (*it == dest_tile)
{

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "StupidAI.h"
#include "../../lib/BattleState.h"
#include "../../CCallback.h"
CStupidAI::CStupidAI(void)
: side(-1), cb(NULL)
@ -33,12 +34,38 @@ void CStupidAI::actionStarted( const BattleAction *action )
BattleAction CStupidAI::activeStack( const CStack * stack )
{
print("activeStack called");
std::vector<THex> avHexes = cb->battleGetAvailableHexes(stack, false);
std::vector<const CStack *> avEnemies;
for(int g=0; g<avHexes.size(); ++g)
{
const CStack * enemy = cb->battleGetStackByPos(avHexes[g]);
if (enemy)
{
avEnemies.push_back(enemy);
}
}
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(avEnemies.size())
{
const CStack * enemy = avEnemies[0];
//shooting
if (cb->battleCanShoot(stack, enemy->position))
{
return BattleAction::makeShotAttack(stack, enemy);
}
//melee
return BattleAction::makeMeleeAttack(stack, enemy, avHexes);
}
return BattleAction::makeDefend(stack);
}

View File

@ -517,10 +517,10 @@ int CBattleCallback::battleMakeAction(BattleAction* action)
return 0;
}
const CStack* CBattleCallback::battleGetStackByPos(int pos, bool onlyAlive)
const CStack* CBattleCallback::battleGetStackByPos(THex pos, bool onlyAlive)
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
return battleGetStackByID(gs->battleGetStack(pos, onlyAlive), onlyAlive);
return gs->curB->battleGetStack(pos, onlyAlive);
}
int CBattleCallback::battleGetPos(int stack)
@ -565,25 +565,25 @@ void CBattleCallback::getStackQueue( std::vector<const CStack *> &out, int howMa
gs->curB->getStackQueue(out, howMany);
}
std::vector<int> CBattleCallback::battleGetAvailableHexes(int ID, bool addOccupiable)
std::vector<THex> CBattleCallback::battleGetAvailableHexes(const CStack * stack, bool addOccupiable)
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!gs->curB)
{
tlog2<<"battleGetAvailableHexes called when there is no battle!"<<std::endl;
return std::vector<int>();
return std::vector<THex>();
}
return gs->curB->getAccessibility(ID, addOccupiable);
return gs->curB->getAccessibility(stack, addOccupiable);
//return gs->battleGetRange(ID);
}
bool CBattleCallback::battleCanShoot(int ID, int dest)
bool CBattleCallback::battleCanShoot(const CStack * stack, THex dest)
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!gs->curB) return false;
return gs->battleCanShoot(ID, dest);
return gs->curB->battleCanShoot(stack, dest);
}
bool CBattleCallback::battleCanCastSpell()
@ -599,15 +599,15 @@ bool CBattleCallback::battleCanCastSpell()
bool CBattleCallback::battleCanFlee()
{
return gs->battleCanFlee(player);
return gs->curB->battleCanFlee(player);
}
const CGTownInstance *CBattleCallback::battleGetDefendedTown()
{
if(!gs->curB || gs->curB->tid == -1)
if(!gs->curB || gs->curB->town == NULL)
return NULL;
return static_cast<const CGTownInstance *>(gs->map->objects[gs->curB->tid].get());
return gs->curB->town;
}
ui8 CBattleCallback::battleGetWallState(int partOfWall)
@ -649,7 +649,7 @@ std::pair<ui32, ui32> CBattleCallback::battleEstimateDamage(int attackerID, int
const CStack * attacker = gs->curB->getStack(attackerID, false),
* defender = gs->curB->getStack(defenderID);
return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0, false);
return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker, defender->position), 0, false);
}
ui8 CBattleCallback::battleGetSiegeLevel()
@ -917,19 +917,19 @@ bool CCallback::hasAccess(int playerId) const
return gs->getPlayerRelations( playerId, player ) || player < 0;
}
si8 CBattleCallback::battleHasDistancePenalty( int stackID, int destHex )
si8 CBattleCallback::battleHasDistancePenalty( const CStack * stack, THex destHex )
{
return gs->curB->hasDistancePenalty(stackID, destHex);
return gs->curB->hasDistancePenalty(stack, destHex);
}
si8 CBattleCallback::battleHasWallPenalty( int stackID, int destHex )
si8 CBattleCallback::battleHasWallPenalty( const CStack * stack, THex destHex )
{
return gs->curB->hasWallPenalty(stackID, destHex);
return gs->curB->hasWallPenalty(stack, destHex);
}
si8 CBattleCallback::battleCanTeleportTo(int stackID, int destHex, int telportLevel)
si8 CBattleCallback::battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel)
{
return gs->curB->canTeleportTo(stackID, destHex, telportLevel);
return gs->curB->canTeleportTo(stack, destHex, telportLevel);
}
int CCallback::getPlayerStatus(int player) const

View File

@ -79,13 +79,13 @@ public:
virtual int battleGetObstaclesAtTile(THex tile)=0; //returns bitfield
virtual std::vector<CObstacleInstance> battleGetAllObstacles()=0; //returns all obstacles on the battlefield
virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID
virtual const CStack * battleGetStackByPos(int pos, bool onlyAlive = true)=0; //returns stack info by given pos
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 void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest
virtual std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
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
@ -94,8 +94,9 @@ public:
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 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(int stackID, int destHex) =0; //checks if given stack has distance penalty
virtual si8 battleHasWallPenalty(int stackID, int destHex) =0; //checks if given stack has wall penalty
virtual si8 battleHasDistancePenalty(const CStack * stack, THex destHex) =0; //checks if given stack has distance penalty
virtual si8 battleHasWallPenalty(const CStack * stack, THex destHex) =0; //checks if given stack has wall penalty
virtual si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) =0; //checks if teleportation of given stack to given position can take place
};
class ICallback : public virtual IBattleCallback
@ -201,28 +202,28 @@ protected:
public:
//battle
int battleGetBattlefieldType(); // 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship
int battleGetObstaclesAtTile(THex tile); //returns bitfield
std::vector<CObstacleInstance> battleGetAllObstacles(); //returns all obstacles on the battlefield
const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID
const CStack * battleGetStackByPos(int pos, bool onlyAlive = true); //returns stack info by given pos
int battleGetPos(int stack); //returns position (tile ID) of stack
int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack
std::vector<const CStack*> battleGetStacks(); //returns stacks on battlefield
void getStackQueue( std::vector<const CStack *> &out, int howMany ); //returns vector of stack in order of their move sequence
std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID
bool battleCanShoot(int ID, int dest); //returns true if unit with id ID can shoot to dest
bool battleCanCastSpell(); //returns true, if caller can cast a spell
bool battleCanFlee(); //returns true if caller can flee from the battle
const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead
ui8 battleGetWallState(int partOfWall); //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); //returns part of destructible wall / gate / keep under given hex or -1 if not found
std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID); //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(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
si8 battleHasDistancePenalty(int stackID, int destHex); //checks if given stack has distance penalty
si8 battleHasWallPenalty(int stackID, int destHex); //checks if given stack has wall penalty
si8 battleCanTeleportTo(int stackID, int destHex, int telportLevel); //checks if teleportation of given stack to given position can take place
int battleGetBattlefieldType() OVERRIDE; // 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship
int battleGetObstaclesAtTile(THex tile) OVERRIDE; //returns bitfield
std::vector<CObstacleInstance> battleGetAllObstacles() OVERRIDE; //returns all obstacles on the battlefield
const CStack * battleGetStackByID(int ID, bool onlyAlive = true) OVERRIDE; //returns stack info by given ID
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
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
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>
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
si8 battleHasWallPenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has wall penalty
si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) OVERRIDE; //checks if teleportation of given stack to given position can take place
friend CCallback;
friend CClient;

View File

@ -603,7 +603,7 @@ bool CBattleStackMoved::init()
Point begPosition = CBattleHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack, owner);
Point endPosition = CBattleHex::getXYUnitAnim(destHex, movedStack->attackerOwned, movedStack, owner);
int mutPos = BattleInfo::mutualPosition(curStackPos, destHex);
int mutPos = THex::mutualPosition(curStackPos, destHex);
//reverse unit if necessary
if((begPosition.x > endPosition.x) && owner->creDir[stack->ID] == true)
@ -879,9 +879,9 @@ bool CMeleeAttack::init()
int reversedShift = 0; //shift of attacking stack's position due to reversing
if(attackingStack->attackerOwned)
{
if(attackingStack->doubleWide() && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
if(attackingStack->doubleWide() && THex::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
{
if(BattleInfo::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
if(THex::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
{
reversedShift = (attackingStack->attackerOwned ? -1 : 1);
}
@ -889,9 +889,9 @@ bool CMeleeAttack::init()
}
else //if(astack->attackerOwned)
{
if(attackingStack->doubleWide() && BattleInfo::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
if(attackingStack->doubleWide() && THex::mutualPosition(attackingStackPosBeforeReturn, dest) == -1)
{
if(BattleInfo::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
if(THex::mutualPosition(attackingStackPosBeforeReturn + (attackingStack->attackerOwned ? -1 : 1), dest) >= 0) //if reversing stack will make its position adjacent to dest
{
reversedShift = (attackingStack->attackerOwned ? -1 : 1);
}
@ -912,7 +912,7 @@ bool CMeleeAttack::init()
static const int mutPosToGroup[] = {11, 11, 12, 13, 13, 12};
int mutPos = BattleInfo::mutualPosition(attackingStackPosBeforeReturn + reversedShift, dest);
int mutPos = THex::mutualPosition(attackingStackPosBeforeReturn + reversedShift, dest);
switch(mutPos) //attack direction
{
case 0: case 1: case 2: case 3: case 4: case 5:
@ -1792,10 +1792,10 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
}
}
else if(curInt->cb->battleCanShoot(activeStack->ID,myNumber)) //we can shoot enemy
else if(curInt->cb->battleCanShoot(activeStack,myNumber)) //we can shoot enemy
{
if(curInt->cb->battleHasDistancePenalty(activeStack->ID, myNumber) ||
curInt->cb->battleHasWallPenalty(activeStack->ID, myNumber))
if(curInt->cb->battleHasDistancePenalty(activeStack, myNumber) ||
curInt->cb->battleHasWallPenalty(activeStack, myNumber))
{
CCS->curh->changeGraphic(1,15);
}
@ -2336,7 +2336,7 @@ bool CBattleInterface::isTileAttackable(const int & number) const
{
for(size_t b=0; b<shadedHexes.size(); ++b)
{
if(BattleInfo::mutualPosition(shadedHexes[b], number) != -1 || shadedHexes[b] == number)
if(THex::mutualPosition(shadedHexes[b], number) != -1 || shadedHexes[b] == number)
return true;
}
return false;
@ -2417,7 +2417,7 @@ void CBattleInterface::hexLclicked(int whichOne)
case 5: //teleport
const CSpell *s = CGI->spellh->spells[spellToCast->additionalInfo];
ui8 skill = getActiveHero()->getSpellSchoolLevel(s); //skill level
if (!curInt->cb->battleCanTeleportTo(activeStack->ID, whichOne, skill))
if (!curInt->cb->battleCanTeleportTo(activeStack, whichOne, skill))
{
allowCasting = false;
}
@ -2441,7 +2441,7 @@ void CBattleInterface::hexLclicked(int whichOne)
CCS->curh->changeGraphic(1, 6); //cursor should be changed
if(activeStack->doubleWide())
{
std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack->ID, false);
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
int shiftedDest = whichOne + (activeStack->attackerOwned ? 1 : -1);
if(vstd::contains(acc, whichOne))
giveCommand(2,whichOne,activeStack->ID);
@ -2459,7 +2459,7 @@ void CBattleInterface::hexLclicked(int whichOne)
}
}
else if(dest->owner != actSt->owner
&& curInt->cb->battleCanShoot(activeStack->ID, whichOne) ) //shooting
&& curInt->cb->battleCanShoot(activeStack, whichOne) ) //shooting
{
CCS->curh->changeGraphic(1, 6); //cursor should be changed
giveCommand(7,whichOne,activeStack->ID);
@ -2510,7 +2510,7 @@ void CBattleInterface::hexLclicked(int whichOne)
{
if(actStack->doubleWide() && !actStack->attackerOwned)
{
std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack->ID, false);
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne - 1;
else
@ -2562,7 +2562,7 @@ void CBattleInterface::hexLclicked(int whichOne)
{
if(actStack->doubleWide() && actStack->attackerOwned)
{
std::vector<int> acc = curInt->cb->battleGetAvailableHexes(activeStack->ID, false);
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne + 1;
else
@ -3120,7 +3120,7 @@ void CBattleInterface::showPieceOfWall(SDL_Surface * to, int hex, const std::vec
void CBattleInterface::redrawBackgroundWithHexes(const CStack * activeStack)
{
shadedHexes = curInt->cb->battleGetAvailableHexes(activeStack->ID, true);
shadedHexes = curInt->cb->battleGetAvailableHexes(activeStack, true);
//preparating background graphic with hexes and shaded hexes
blitAt(background, 0, 0, backgroundWithHexes);

View File

@ -394,7 +394,7 @@ private:
const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; NULL of none
void activateStack(); //sets activeStack to stackToActivate etc.
int mouseHoveredStack; //stack hovered by mouse; if -1 -> none
std::vector<int> shadedHexes; //hexes available for active stack
std::vector<THex> shadedHexes; //hexes available for active stack
int previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago
int currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon)
float getAnimSpeedMultiplier() const; //returns multiplier for number of frames in a group

View File

@ -834,10 +834,10 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
else
{//WARNING: does not support multiple attacked creatures
int shift = 0;
if(ba->counter() && BattleInfo::mutualPosition(curAction->destinationTile, attacker->position) < 0)
if(ba->counter() && THex::mutualPosition(curAction->destinationTile, attacker->position) < 0)
{
int distp = BattleInfo::getDistance(curAction->destinationTile + 1, attacker->position);
int distm = BattleInfo::getDistance(curAction->destinationTile - 1, attacker->position);
int distp = THex::getDistance(curAction->destinationTile + 1, attacker->position);
int distm = THex::getDistance(curAction->destinationTile - 1, attacker->position);
if( distp < distm )
shift = 1;

159
global.h
View File

@ -7,6 +7,7 @@
#include <boost/logic/tribool.hpp>
using boost::logic::tribool;
#include <boost/cstdint.hpp>
#include <assert.h>
typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes)
typedef boost::uint32_t ui32; //unsigned int 32 bits (4 bytes)
typedef boost::uint16_t ui16; //unsigned int 16 bits (2 bytes)
@ -17,7 +18,8 @@ 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 ui16 THex; //for battle stacks' positions
#include "int3.h"
#include <map>
#include <vector>
@ -126,6 +128,161 @@ const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
const int SPELLBOOK_GOLD_COST = 500;
//for battle stacks' positions
struct THex
{
enum EDir{RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT};
si16 hex;
THex() : hex(-1) {}
THex(si16 _hex) : hex(_hex)
{
assert(hex >= 0 && hex < BFIELD_SIZE);
}
operator si16() const
{
return hex;
}
template<typename inttype>
THex(inttype x, inttype y)
{
setXY(x, y);
}
template<typename inttype>
THex(std::pair<inttype, inttype> xy)
{
setXY(xy);
}
template<typename inttype>
void setX(inttype x)
{
setXY(x, getY());
}
template<typename inttype>
void setY(inttype y)
{
setXY(getX(), y);
}
void setXY(si16 x, si16 y)
{
assert(x >= 0 && x < BFIELD_WIDTH && y >= 0 && y < BFIELD_HEIGHT);
hex = x + y * BFIELD_WIDTH;
}
template<typename inttype>
void setXY(std::pair<inttype, inttype> xy)
{
setXY(xy.first, xy.second);
}
si16 getY() const
{
return hex/BFIELD_WIDTH;
}
si16 getX() const
{
int pos = hex - getY() * BFIELD_WIDTH;
return pos;
}
std::pair<si16, si16> getXY() const
{
return std::make_pair(getX(), getY());
}
//moving to direction
void operator+=(EDir dir)
{
si16 x = getX(),
y = getY();
switch(dir)
{
case TOP_LEFT:
setXY(y%2 ? x-1 : x, y-1);
case TOP_RIGHT:
setXY(y%2 ? x : x+1, y-1);
case RIGHT:
setXY(x+1, y);
case BOTTOM_RIGHT:
setXY(y%2 ? x : x+1, y+1);
case BOTTOM_LEFT:
setXY(y%2 ? x-1 : x, y+1);
case LEFT:
setXY(x-1, y);
default:
throw std::string("Disaster: wrong direction in THex::operator+=!\n");
}
}
//generates new THex moved by given dir
THex operator+(EDir dir) const
{
THex ret(*this);
ret += dir;
return ret;
}
std::vector<THex> neighbouringTiles() const
{
std::vector<THex> ret;
const int WN = BFIELD_WIDTH;
checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), ret);
checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), ret);
checkAndPush(hex - 1, ret);
checkAndPush(hex + 1, ret);
checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), ret);
checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), ret);
return ret;
}
//returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
static signed char mutualPosition(THex hex1, THex hex2)
{
if(hex2 == hex1 - ( (hex1/17)%2 ? 18 : 17 )) //top left
return 0;
if(hex2 == hex1 - ( (hex1/17)%2 ? 17 : 16 )) //top right
return 1;
if(hex2 == hex1 - 1 && hex1%17 != 0) //left
return 5;
if(hex2 == hex1 + 1 && hex1%17 != 16) //right
return 2;
if(hex2 == hex1 + ( (hex1/17)%2 ? 16 : 17 )) //bottom left
return 4;
if(hex2 == hex1 + ( (hex1/17)%2 ? 17 : 18 )) //bottom right
return 3;
return -1;
}
//returns distance between given hexes
static si8 getDistance(THex hex1, THex hex2)
{
int xDst = std::abs(hex1 % BFIELD_WIDTH - hex2 % BFIELD_WIDTH),
yDst = std::abs(hex1 / BFIELD_WIDTH - hex2 / BFIELD_WIDTH);
return std::max(xDst, yDst) + std::min(xDst, yDst) - (yDst + 1)/2;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & hex;
}
private:
static void checkAndPush(int tile, std::vector<THex> & ret)
{
if( tile>=0 && tile<BFIELD_SIZE && (tile%BFIELD_WIDTH != (BFIELD_WIDTH - 1)) && (tile%BFIELD_WIDTH != 0) )
ret.push_back(THex(tile));
}
};
enum EMarketMode
{
RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT,

View File

@ -30,12 +30,31 @@ BattleAction BattleAction::makeDefend(const CStack *stack)
return ba;
}
BattleAction BattleAction::makeMeleeAttack(const CStack *stack) /*WARNING: stacks must be neighbouring! */
BattleAction BattleAction::makeMeleeAttack( const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker )
{
BattleAction ba;
ba.side = !stack->attackerOwned;
ba.actionType = WAIT;
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.additionalInfo = attacked->position;
return ba;
}

View File

@ -23,8 +23,7 @@ struct DLL_EXPORT BattleAction
{
INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
};
ui8 actionType; // 0 = No action; 1 = Hero cast a spell 2 = Walk 3 = Defend 4 = Retreat from the battle
//5 = Surrender 6 = Walk and Attack 7 = Shoot 8 = Wait 9 = Catapult
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;
si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
@ -37,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); //WARNING: stacks must be neighbouring!;
static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, std::vector<THex> reachableByAttacker);
static BattleAction makeShotAttack(const CStack *shooter, const CStack *target);
static BattleAction makeMove(const CStack *stack, THex dest);
};

View File

@ -129,7 +129,7 @@ const CStack * BattleInfo::getStackT(THex tileID, bool onlyAlive) const
return const_cast<BattleInfo * const>(this)->getStackT(tileID, onlyAlive);
}
void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit) const
void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<THex> & occupyable, bool flying, const CStack * stackToOmmit) const
{
memset(accessibility, 1, BFIELD_SIZE); //initialize array with trues
@ -142,7 +142,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
for(unsigned int g=0; g<stacks.size(); ++g)
{
if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit || stacks[g]->position < 0) //we don't want to lock position of this stack (eg. if it's a turret)
if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit->ID || stacks[g]->position < 0) //we don't want to lock position of this stack (eg. if it's a turret)
continue;
accessibility[stacks[g]->position] = false;
@ -174,8 +174,9 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
accessibility[permanentlyLocked[b]] = false;
}
static const std::pair<int, int> lockedIfNotDestroyed[] = //(which part of wall, which hex is blocked if this part of wall is not destroyed
{std::make_pair(2, 182), std::make_pair(3, 130), std::make_pair(4, 62), std::make_pair(5, 29)};
static const std::pair<int, THex> lockedIfNotDestroyed[] = //(which part of wall, which hex is blocked if this part of wall is not destroyed
{std::make_pair(2, THex(182)), std::make_pair(3, THex(130)),
std::make_pair(4, THex(62)), std::make_pair(5, THex(29))};
for(int b=0; b<ARRAY_COUNT(lockedIfNotDestroyed); ++b)
{
if(si.wallState[lockedIfNotDestroyed[b].first] < 3)
@ -194,12 +195,12 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
//occupyability
if(addOccupiable && twoHex)
{
std::set<int> rem; //tiles to unlock
std::set<THex> rem; //tiles to unlock
for(int h=0; h<BFIELD_HEIGHT; ++h)
{
for(int w=1; w<BFIELD_WIDTH-1; ++w)
{
int hex = h * BFIELD_WIDTH + w;
THex hex(w, h);
if(!isAccessible(hex, accessibility, twoHex, attackerOwned, flying, true)
&& (attackerOwned ? isAccessible(hex+1, accessibility, twoHex, attackerOwned, flying, true) : isAccessible(hex-1, accessibility, twoHex, attackerOwned, flying, true) )
)
@ -230,7 +231,7 @@ bool BattleInfo::isAccessible(int hex, bool * accessibility, bool twoHex, bool a
}
}
void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const //both pointers must point to the at least 187-elements int arrays
void BattleInfo::makeBFS(THex start, bool *accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const //both pointers must point to the at least 187-elements int arrays
{
//inits
for(int b=0; b<BFIELD_SIZE; ++b)
@ -238,14 +239,14 @@ void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *
for(int g=0; g<BFIELD_SIZE; ++g)
dists[g] = 100000000;
std::queue< std::pair<int, bool> > hexq; //bfs queue <hex, accessible> (second filed used only if fillPredecessors is true)
std::queue< std::pair<THex, bool> > hexq; //bfs queue <hex, accessible> (second filed used only if fillPredecessors is true)
hexq.push(std::make_pair(start, true));
dists[hexq.front().first] = 0;
int curNext = -1; //for bfs loop only (helper var)
while(!hexq.empty()) //bfs loop
{
std::pair<int, bool> curHex = hexq.front();
std::vector<int> neighbours = neighbouringTiles(curHex.first);
std::pair<THex, bool> curHex = hexq.front();
std::vector<THex> neighbours = curHex.first.neighbouringTiles();
hexq.pop();
for(unsigned int nr=0; nr<neighbours.size(); nr++)
{
@ -268,34 +269,33 @@ void BattleInfo::makeBFS(int start, bool *accessibility, int *predecessor, int *
}
};
std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) const
std::vector<THex> BattleInfo::getAccessibility(const CStack * stack, bool addOccupiable) const
{
std::vector<int> ret;
std::vector<THex> ret;
bool ac[BFIELD_SIZE];
const CStack *s = getStack(stackID, false); //this function is called from healedOrResurrected, so our stack can be dead
if(s->position < 0) //turrets
return std::vector<int>();
if(stack->position < 0) //turrets
return std::vector<THex>();
std::set<int> occupyable;
std::set<THex> occupyable;
getAccessibilityMap(ac, s->doubleWide(), s->attackerOwned, addOccupiable, occupyable, s->hasBonusOfType(Bonus::FLYING), stackID);
getAccessibilityMap(ac, stack->doubleWide(), stack->attackerOwned, addOccupiable, occupyable, stack->hasBonusOfType(Bonus::FLYING), stack);
int pr[BFIELD_SIZE], dist[BFIELD_SIZE];
makeBFS(s->position, ac, pr, dist, s->doubleWide(), s->attackerOwned, s->hasBonusOfType(Bonus::FLYING), false);
makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
if(s->doubleWide())
if(stack->doubleWide())
{
if(!addOccupiable)
{
std::vector<int> rem;
std::vector<THex> rem;
for(int b=0; b<BFIELD_SIZE; ++b)
{
//don't take into account most left and most right columns of hexes
if( b % BFIELD_WIDTH == 0 || b % BFIELD_WIDTH == BFIELD_WIDTH - 1 )
continue;
if( ac[b] && !(s->attackerOwned ? ac[b-1] : ac[b+1]) )
if( ac[b] && !(stack->attackerOwned ? ac[b-1] : ac[b+1]) )
{
rem.push_back(b);
}
@ -308,16 +308,16 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
//removing accessibility for side hexes
for(int v=0; v<BFIELD_SIZE; ++v)
if(s->attackerOwned ? (v%BFIELD_WIDTH)==1 : (v%BFIELD_WIDTH)==(BFIELD_WIDTH - 2))
if(stack->attackerOwned ? (v%BFIELD_WIDTH)==1 : (v%BFIELD_WIDTH)==(BFIELD_WIDTH - 2))
ac[v] = false;
}
}
for (int i=0; i < BFIELD_SIZE ; ++i) {
if(
( ( !addOccupiable && dist[i] <= s->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= s->Speed() && isAccessible(i, ac, s->doubleWide(), s->attackerOwned, s->hasBonusOfType(Bonus::FLYING), true) ) )//we can reach it
|| (vstd::contains(occupyable, i) && ( dist[ i + (s->attackerOwned ? 1 : -1 ) ] <= s->Speed() ) &&
ac[i + (s->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
( ( !addOccupiable && dist[i] <= stack->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= stack->Speed() && isAccessible(i, ac, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), true) ) )//we can reach it
|| (vstd::contains(occupyable, i) && ( dist[ i + (stack->attackerOwned ? 1 : -1 ) ] <= stack->Speed() ) &&
ac[i + (stack->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex
)
{
ret.push_back(i);
@ -326,65 +326,35 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
return ret;
}
bool BattleInfo::isStackBlocked(int ID)
bool BattleInfo::isStackBlocked(const CStack * stack)
{
CStack *our = getStack(ID);
if(our->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
return false;
for(unsigned int i=0; i<stacks.size();i++)
{
if( !stacks[i]->alive()
|| stacks[i]->owner==our->owner
|| stacks[i]->owner==stack->owner
)
continue; //we omit dead and allied stacks
if(stacks[i]->doubleWide())
{
if( mutualPosition(stacks[i]->position, our->position) >= 0
|| mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), our->position) >= 0)
if( THex::mutualPosition(stacks[i]->position, stack->position) >= 0
|| THex::mutualPosition(stacks[i]->position + (stacks[i]->attackerOwned ? -1 : 1), stack->position) >= 0)
return true;
}
else
{
if( mutualPosition(stacks[i]->position, our->position) >= 0 )
if( THex::mutualPosition(stacks[i]->position, stack->position) >= 0 )
return true;
}
}
return false;
}
signed char BattleInfo::mutualPosition(THex hex1, THex hex2)
{
if(hex2 == hex1 - ( (hex1/17)%2 ? 18 : 17 )) //top left
return 0;
if(hex2 == hex1 - ( (hex1/17)%2 ? 17 : 16 )) //top right
return 1;
if(hex2 == hex1 - 1 && hex1%17 != 0) //left
return 5;
if(hex2 == hex1 + 1 && hex1%17 != 16) //right
return 2;
if(hex2 == hex1 + ( (hex1/17)%2 ? 16 : 17 )) //bottom left
return 4;
if(hex2 == hex1 + ( (hex1/17)%2 ? 17 : 18 )) //bottom right
return 3;
return -1;
}
std::vector<int> BattleInfo::neighbouringTiles(int hex)
{
#define CHECK_AND_PUSH(tile) {int hlp = (tile); if(hlp>=0 && hlp<BFIELD_SIZE && (hlp%BFIELD_WIDTH!=16) && hlp%BFIELD_WIDTH) ret.push_back(hlp);}
std::vector<int> ret;
CHECK_AND_PUSH(hex - ( (hex/17)%2 ? 18 : 17 ));
CHECK_AND_PUSH(hex - ( (hex/17)%2 ? 17 : 16 ));
CHECK_AND_PUSH(hex - 1);
CHECK_AND_PUSH(hex + 1);
CHECK_AND_PUSH(hex + ( (hex/17)%2 ? 16 : 17 ));
CHECK_AND_PUSH(hex + ( (hex/17)%2 ? 17 : 18 ));
#undef CHECK_AND_PUSH
return ret;
}
std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned)
{
{
int predecessor[BFIELD_SIZE]; //for getting the Path
int dist[BFIELD_SIZE]; //calculated distances
@ -585,7 +555,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, con
//wall / distance penalty + advanced air shield
if (shooting && !NBonus::hasOfType(attackerHero, Bonus::NO_SHOTING_PENALTY) && (
hasDistancePenalty(attacker->ID, defender->position) || hasWallPenalty(attacker->ID, defender->position) ||
hasDistancePenalty(attacker, defender->position) || hasWallPenalty(attacker, defender->position) ||
HLP::hasAdvancedAirShield(defender) )
)
{
@ -780,7 +750,7 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster)
return ret + manaReduction + manaIncrease;
}
int BattleInfo::hexToWallPart(int hex) const
int BattleInfo::hexToWallPart(THex hex) const
{
if(siege == 0) //there is no battle!
return -1;
@ -808,9 +778,9 @@ int BattleInfo::lineToWallHex( int line ) const
std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
{
bool ac[BFIELD_SIZE];
std::set<int> occupyable;
std::set<THex> occupyable;
getAccessibilityMap(ac, closest->doubleWide(), closest->attackerOwned, false, occupyable, closest->hasBonusOfType(Bonus::FLYING), closest->ID);
getAccessibilityMap(ac, closest->doubleWide(), closest->attackerOwned, false, occupyable, closest->hasBonusOfType(Bonus::FLYING), closest);
int predecessor[BFIELD_SIZE], dist[BFIELD_SIZE];
makeBFS(closest->position, ac, predecessor, dist, closest->doubleWide(), closest->attackerOwned, closest->hasBonusOfType(Bonus::FLYING), true);
@ -1051,15 +1021,13 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
}
}
si8 BattleInfo::hasDistancePenalty( int stackID, int destHex )
si8 BattleInfo::hasDistancePenalty( const CStack * stack, THex destHex )
{
const CStack * stack = getStack(stackID);
struct HLP
{
static bool lowerAnalyze(const CStack * stack, int hex)
static bool lowerAnalyze(const CStack * stack, THex hex)
{
int distance = BattleInfo::getDistance(hex, stack->position);
int distance = THex::getDistance(hex, stack->position);
//I hope it's approximately correct
return distance > 10 && !stack->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY);
@ -1085,13 +1053,12 @@ si8 BattleInfo::sameSideOfWall(int pos1, int pos2)
return stackLeft != destLeft;
}
si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex )
{
if (siege == 0)
{
return false;
}
const CStack * stack = getStack(stackID);
if (stack->hasBonusOfType(Bonus::NO_WALL_PENALTY))
{
return false;
@ -1100,18 +1067,17 @@ si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
return !sameSideOfWall(stack->position, destHex);
}
si8 BattleInfo::canTeleportTo(int stackID, int destHex, int telportLevel)
si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel)
{
bool ac[BFIELD_SIZE];
const CStack *s = getStack(stackID, false); //this function is called from healedOrResurrected, so our stack can be dead
std::set<int> occupyable;
std::set<THex> occupyable;
getAccessibilityMap(ac, s->doubleWide(), s->attackerOwned, false, occupyable, s->hasBonusOfType(Bonus::FLYING), stackID);
getAccessibilityMap(ac, stack->doubleWide(), stack->attackerOwned, false, occupyable, stack->hasBonusOfType(Bonus::FLYING), stack);
if (siege && telportLevel < 2) //check for wall
{
return ac[destHex] && sameSideOfWall(s->position, destHex);
return ac[destHex] && sameSideOfWall(stack->position, destHex);
}
else
{
@ -1141,11 +1107,103 @@ si8 BattleInfo::canTeleportTo(int stackID, int destHex, int telportLevel)
// }
// }
si8 BattleInfo::getDistance( THex hex1, THex hex2 )
bool BattleInfo::battleCanShoot(const CStack * stack, THex dest)
{
int xDst = std::abs(hex1 % BFIELD_WIDTH - hex2 % BFIELD_WIDTH),
yDst = std::abs(hex1 / BFIELD_WIDTH - hex2 / BFIELD_WIDTH);
return std::max(xDst, yDst) + std::min(xDst, yDst) - (yDst + 1)/2;
const CStack *dst = getStackT(dest);
if(!stack || !dst) return false;
const CGHeroInstance * stackHero = battleGetOwner(stack);
if(stack->hasBonusOfType(Bonus::FORGETFULL)) //forgetfulness
return false;
if(stack->getCreature()->idNumber == 145 && dst) //catapult cannot attack creatures
return false;
if(stack->hasBonusOfType(Bonus::SHOOTER)//it's shooter
&& stack->owner != dst->owner
&& dst->alive()
&& (!isStackBlocked(stack) || NBonus::hasOfType(stackHero, Bonus::FREE_SHOOTING))
&& stack->shots
)
return true;
return false;
}
bool BattleInfo::battleCanFlee(int player)
{
if (player == side1)
{
if (!heroes[0])
return false;//current player have no hero
}
else
{
if (!heroes[1])
return false;
}
if( ( heroes[0] && heroes[0]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE) ) //eg. one of heroes is wearing shakles of war
|| ( heroes[1] && heroes[1]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE)))
return false;
if (player == side2 && siege //defender in siege
&& !(town->subID == 6 && vstd::contains(town->builtBuildings, 17)))//without escape tunnel
return false;
return true;
}
const CStack * BattleInfo::battleGetStack(THex pos, bool onlyAlive)
{
for(unsigned int g=0; g<stacks.size(); ++g)
{
if((stacks[g]->position == pos
|| (stacks[g]->doubleWide()
&&( (stacks[g]->attackerOwned && stacks[g]->position-1 == pos)
|| (!stacks[g]->attackerOwned && stacks[g]->position+1 == pos) )
))
&& (!onlyAlive || stacks[g]->alive())
)
return stacks[g];
}
return NULL;
}
const CGHeroInstance * BattleInfo::battleGetOwner(const CStack * stack)
{
return heroes[!stack->attackerOwned];
}
si8 BattleInfo::battleMaxSpellLevel()
{
// if(!curB) //there is not battle
// {
// tlog1 << "si8 CGameState::maxSpellLevel() call when there is no battle!" << std::endl;
// throw "si8 CGameState::maxSpellLevel() call when there is no battle!";
// }
si8 levelLimit = SPELL_LEVELS;
const CGHeroInstance *h1 = heroes[0];
if(h1)
{
BOOST_FOREACH(const Bonus *i, h1->bonuses)
if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
amin(levelLimit, i->val);
}
const CGHeroInstance *h2 = heroes[1];
if(h2)
{
BOOST_FOREACH(const Bonus *i, h2->bonuses)
if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
amin(levelLimit, i->val);
}
return levelLimit;
}
void BattleInfo::localInit()
@ -1210,12 +1268,12 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
if(town)
{
curB->tid = town->id;
curB->town = town;
curB->siege = town->fortLevel();
}
else
{
curB->tid = -1;
curB->town = NULL;
curB->siege = 0;
}
@ -1279,11 +1337,11 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
{
if((stacks[g]->position%17)==1 && stacks[g]->doubleWide() && stacks[g]->attackerOwned)
{
stacks[g]->position += 1;
stacks[g]->position += THex::RIGHT;
}
else if((stacks[g]->position%17)==15 && stacks[g]->doubleWide() && !stacks[g]->attackerOwned)
{
stacks[g]->position -= 1;
stacks[g]->position += THex::LEFT;
}
}
@ -1527,7 +1585,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), position(-1),
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
counterAttacks(1)
{
assert(base);
@ -1541,7 +1599,7 @@ CStack::CStack()
}
CStack::CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S)
: base(NULL), ID(I), owner(O), slot(S), attackerOwned(AO), position(-1), counterAttacks(1)
: base(NULL), ID(I), owner(O), slot(S), attackerOwned(AO), counterAttacks(1)
{
type = stack->type;
count = baseAmount = stack->count;
@ -1557,7 +1615,7 @@ void CStack::init()
owner = 255;
slot = 255;
attackerOwned = false;
position = -1;
position = THex();
counterAttacks = -1;
}

View File

@ -50,7 +50,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
ui8 side1, side2; //side1 - attacker, side2 - defender
si32 round, activeStack;
ui8 siege; // = 0 ordinary battle = 1 a siege with a Fort = 2 a siege with a Citadel = 3 a siege with a Castle
si32 tid; //used during town siege - id of attacked town; -1 if not town defence
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
@ -62,7 +62,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
template <typename Handler> void serialize(Handler &h, const int version)
{
h & side1 & side2 & round & activeStack & siege & tid & tile & stacks & belligerents & obstacles
h & side1 & side2 & round & activeStack & siege & town & tile & stacks & belligerents & obstacles
& castSpells & si & battlefieldType;
h & heroes;
h & static_cast<CBonusSystemNode&>(*this);
@ -78,16 +78,14 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
const CStack * getStack(int stackID, bool onlyAlive = true) const;
CStack * getStackT(THex tileID, bool onlyAlive = true);
const CStack * getStackT(THex tileID, bool onlyAlive = true) const;
void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit=-1) const; //send pointer to at least 187 allocated bytes
void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<THex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
static bool isAccessible(int hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
void makeBFS(int start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
void makeBFS(THex start, bool*accessibility, int *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
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<int> getAccessibility(int stackID, bool addOccupiable) const; //returns vector of accessible tiles (taking into account the creature range)
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(int ID); //returns true if there is neighboring enemy stack
static signed char mutualPosition(THex hex1, THex hex2); //returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
static std::vector<int> neighbouringTiles(int hex);
static si8 getDistance(THex hex1, THex hex2); //returns distance between given hexes
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>
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
@ -96,16 +94,22 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
int hexToWallPart(int hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
int hexToWallPart(THex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
int lineToWallHex(int line) const; //returns hex with wall in given line
std::pair<const CStack *, int> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
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(int stackID, int destHex); //determines if given stack has distance penalty shooting given pos
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(int stackID, int destHex); //determines if given stack has wall penalty shooting given pos
si8 canTeleportTo(int stackID, int destHex, int telportLevel); //determines if given stack can teleport to given place
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
bool battleCanFlee(int player); //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
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 );
};

View File

@ -1542,51 +1542,6 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
CGTeleport::postInit(); //pairing subterranean gates
}
bool CGameState::battleCanFlee(int player)
{
if(!curB) //there is no battle
return false;
if (player == curB->side1)
{
if (!curB->heroes[0])
return false;//current player have no hero
}
else
{
if (!curB->heroes[1])
return false;
}
if( ( curB->heroes[0] && curB->heroes[0]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE) ) //eg. one of heroes is wearing shakles of war
|| ( curB->heroes[1] && curB->heroes[1]->hasBonusOfType(Bonus::ENEMY_CANT_ESCAPE)))
return false;
if (player == curB->side2 && curB->siege //defender in siege
&& !(getTown(curB->tid)->subID == 6 && vstd::contains(getTown(curB->tid)->builtBuildings, 17)))//without escape tunnel
return false;
return true;
}
int CGameState::battleGetStack(int pos, bool onlyAlive)
{
if(!curB)
return -1;
for(unsigned int g=0; g<curB->stacks.size(); ++g)
{
if((curB->stacks[g]->position == pos
|| (curB->stacks[g]->doubleWide()
&&( (curB->stacks[g]->attackerOwned && curB->stacks[g]->position-1 == pos)
|| (!curB->stacks[g]->attackerOwned && curB->stacks[g]->position+1 == pos) )
))
&& (!onlyAlive || curB->stacks[g]->alive())
)
return curB->stacks[g]->ID;
}
return -1;
}
int CGameState::battleGetBattlefieldType(int3 tile)
{
if(tile==int3() && curB)
@ -1659,14 +1614,6 @@ int CGameState::battleGetBattlefieldType(int3 tile)
}
}
const CGHeroInstance * CGameState::battleGetOwner(int stackID)
{
if(!curB)
return NULL;
return curB->heroes[!curB->getStack(stackID)->attackerOwned];
}
std::set<std::pair<int, int> > costDiff(const std::vector<ui32> &a, const std::vector<ui32> &b, const int modifier = 100) //modifer %
{
std::set<std::pair<int, int> > ret;
@ -2503,63 +2450,7 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
}
return true;
}
si8 CGameState::battleMaxSpellLevel()
{
if(!curB) //there is not battle
{
tlog1 << "si8 CGameState::maxSpellLevel() call when there is no battle!" << std::endl;
throw "si8 CGameState::maxSpellLevel() call when there is no battle!";
}
si8 levelLimit = SPELL_LEVELS;
const CGHeroInstance *h1 = curB->heroes[0];
if(h1)
{
BOOST_FOREACH(const Bonus *i, h1->bonuses)
if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
amin(levelLimit, i->val);
}
const CGHeroInstance *h2 = curB->heroes[1];
if(h2)
{
BOOST_FOREACH(const Bonus *i, h2->bonuses)
if(i->type == Bonus::BLOCK_SPELLS_ABOVE_LEVEL)
amin(levelLimit, i->val);
}
return levelLimit;
}
bool CGameState::battleCanShoot(int ID, int dest)
{
if(!curB)
return false;
const CStack *our = curB->getStack(ID),
*dst = curB->getStackT(dest);
if(!our || !dst) return false;
const CGHeroInstance * ourHero = battleGetOwner(our->ID);
if(our->hasBonusOfType(Bonus::FORGETFULL)) //forgetfulness
return false;
if(our->getCreature()->idNumber == 145 && dst) //catapult cannot attack creatures
return false;
if(our->hasBonusOfType(Bonus::SHOOTER)//it's shooter
&& our->owner != dst->owner
&& dst->alive()
&& (!curB->isStackBlocked(ID) || NBonus::hasOfType(ourHero, Bonus::FREE_SHOOTING))
&& our->shots
)
return true;
return false;
}
int CGameState::victoryCheck( ui8 player ) const
{

View File

@ -295,12 +295,7 @@ public:
CGTownInstance *getTown(int objid);
const CGHeroInstance *getHero(int objid) const;
const CGTownInstance *getTown(int objid) const;
bool battleCanFlee(int player); //returns true if player can flee from the battle
int battleGetStack(int pos, bool onlyAlive); //returns ID of stack at given tile
int battleGetBattlefieldType(int3 tile = int3());// 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship
const CGHeroInstance * battleGetOwner(int stackID); //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
bool battleCanShoot(int ID, int dest); //determines if stack with given ID shoot at the selected destination
UpgradeInfo getUpgradeInfo(const CStackInstance &stack);
int getPlayerRelations(ui8 color1, ui8 color2);// 0 = enemy, 1 = ally, 2 = same player
//float getMarketEfficiency(int player, int mode=0);

View File

@ -615,6 +615,7 @@ DLL_EXPORT const CArtifactInstance *ArtifactLocation::getArt() const
}
}
return NULL;
return NULL;
}
DLL_EXPORT CArtifactInstance *ArtifactLocation::getArt()
@ -1105,7 +1106,7 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs )
int pos; //position of stack on the battlefield - to be calculated
bool ac[BFIELD_SIZE];
std::set<int> occupyable;
std::set<THex> occupyable;
bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
bool flying = VLC->creh->creatures[creID]->isFlying();// vstd::contains(VLC->creh->creatures[creID]->bonuses, Bonus::FLYING);
gs->curB->getAccessibilityMap(ac, twoHex, !side, true, occupyable, flying);
@ -1194,7 +1195,7 @@ DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs )
CStack * changedStack = gs->curB->getStack(healedStacks[g].stackID, false);
//checking if we resurrect a stack that is under a living stack
std::vector<int> access = gs->curB->getAccessibility(changedStack->ID, true);
std::vector<THex> access = gs->curB->getAccessibility(changedStack, true);
bool acc[BFIELD_SIZE];
for(int h=0; h<BFIELD_SIZE; ++h)
acc[h] = false;

View File

@ -476,7 +476,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.flags |= 4;
}
bsa->damageAmount = gs->curB->calculateDmg(att, def, gs->battleGetOwner(att->ID), gs->battleGetOwner(def->ID), bat.shot(), distance, bat.lucky());//counting dealt damage
bsa->damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky());//counting dealt damage
int dmg = bsa->damageAmount;
@ -584,7 +584,7 @@ int CGameHandler::moveStack(int stack, int dest)
//initing necessary tables
bool accessibility[BFIELD_SIZE];
std::vector<int> accessible = gs->curB->getAccessibility(curStack->ID, false);
std::vector<THex> accessible = gs->curB->getAccessibility(curStack, false);
for(int b=0; b<BFIELD_SIZE; ++b)
{
accessibility[b] = false;
@ -613,7 +613,7 @@ int CGameHandler::moveStack(int stack, int dest)
return 0;
bool accessibilityWithOccupyable[BFIELD_SIZE];
std::vector<int> accOc = gs->curB->getAccessibility(curStack->ID, true);
std::vector<THex> accOc = gs->curB->getAccessibility(curStack, true);
for(int b=0; b<BFIELD_SIZE; ++b)
{
accessibilityWithOccupyable[b] = false;
@ -3042,7 +3042,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
}
case 4: //retreat/flee
{
if( !gs->battleCanFlee(ba.side ? gs->curB->side2 : gs->curB->side1) )
if( !gs->curB->battleCanFlee(ba.side ? gs->curB->side2 : gs->curB->side1) )
break;
//TODO: remove retreating hero from map and place it in recruitment list
BattleResult *br = new BattleResult;
@ -3097,13 +3097,13 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
if( !(
(BattleInfo::mutualPosition(curpos, enemypos) >= 0) //front <=> front
(THex::mutualPosition(curpos, enemypos) >= 0) //front <=> front
|| (curStack->doubleWide() //back <=> front
&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos) >= 0)
|| (stackAtEnd->doubleWide() //front <=> back
&& BattleInfo::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
&& THex::mutualPosition(curpos, enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
|| (stackAtEnd->doubleWide() && curStack->doubleWide()//back <=> back
&& BattleInfo::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
&& THex::mutualPosition(curpos + (curStack->attackerOwned ? -1 : 1), enemypos + (stackAtEnd->attackerOwned ? -1 : 1)) >= 0)
)
)
{
@ -3156,7 +3156,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{
CStack *curStack = gs->curB->getStack(ba.stackNumber),
*destStack= gs->curB->getStackT(ba.destinationTile);
if( !gs->battleCanShoot(ba.stackNumber, ba.destinationTile) )
if( !gs->curB->battleCanShoot(curStack, ba.destinationTile) )
break;
sendAndApply(&StartAction(ba)); //start shooting
@ -3742,7 +3742,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
|| (ba.additionalInfo < 10) //it's adventure spell (not combat)
|| (gs->curB->castSpells[ba.side]) //spell has been cast
|| (NBonus::hasOfType(secondHero, Bonus::SPELL_IMMUNITY, s->id)) //non - casting hero provides immunity for this spell
|| (gs->battleMaxSpellLevel() < s->level) //non - casting hero stops caster from casting this spell
|| (gs->curB->battleMaxSpellLevel() < s->level) //non - casting hero stops caster from casting this spell
)
{
tlog2 << "Spell cannot be cast!\n";
@ -4821,7 +4821,7 @@ void CGameHandler::runBattle()
continue;
}
const CGHeroInstance * curOwner = gs->battleGetOwner(next->ID);
const CGHeroInstance * curOwner = gs->curB->battleGetOwner(next);
if( (next->position < 0 && (!curOwner || curOwner->getSecSkillLevel(CGHeroInstance::BALLISTICS) == 0)) //arrow turret, hero has no ballistics
|| (next->getCreature()->idNumber == 146 && (!curOwner || curOwner->getSecSkillLevel(CGHeroInstance::ARTILLERY) == 0))) //ballista, hero has no artillery