1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* New files for lib: CBattleCallback.cpp and CBattleCallback.h

* Updated MSVC project files
* Filesystem: My version of .SND archive does not have \0 after WAV extension. Fixed (though hardcoded 3-char extension length).
* New bonus types: BLOCK_MAGIC_ABOVE for blocking casting spells above given level and BLOCK_ALL_MAGIC for blocking all magic.
* Heavy rewrite of battle callbacks. Fixed some minor bugs. Code reusage between lib/client/server (removed proxy calls). Better access control and support for various perspectives.
* Fixed #1031
* Fixed Orb of Inhibition and Recanter's Cloak (they were incorrectly implemented). Fixed #97.
* Fleeing hero won't lose artifacts. Spellbook won't be captured. Fixed #980.
* Fixed crash when attacking stack dies before counterattack (ie. because of Fire Shield)
* Server does some basic checks if action requests during battle are valid
* Minor stuff.
This commit is contained in:
Michał W. Urbańczyk 2012-08-26 09:07:48 +00:00
parent 2dd9d943c9
commit d390113c23
39 changed files with 3069 additions and 2386 deletions

View File

@ -5,7 +5,7 @@
#include "../../CCallback.h" #include "../../CCallback.h"
#include "../../lib/CCreatureHandler.h" #include "../../lib/CCreatureHandler.h"
CBattleCallback * cbc; CPlayerBattleCallback * cbc;
CStupidAI::CStupidAI(void) CStupidAI::CStupidAI(void)
: side(-1), cb(NULL) : side(-1), cb(NULL)
@ -19,7 +19,7 @@ CStupidAI::~CStupidAI(void)
print("destroyed"); print("destroyed");
} }
void CStupidAI::init( CBattleCallback * CB ) void CStupidAI::init( CPlayerBattleCallback * CB )
{ {
print("init called, saving ptr to IBattleCallback"); print("init called, saving ptr to IBattleCallback");
cbc = cb = CB; cbc = cb = CB;
@ -60,7 +60,7 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr); return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
} }
int distToNearestNeighbour(BattleHex hex, const std::vector<int> & dists, BattleHex *chosenHex = NULL) int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = NULL)
{ {
int ret = 1000000; int ret = 1000000;
BOOST_FOREACH(BattleHex n, hex.neighbouringTiles()) BOOST_FOREACH(BattleHex n, hex.neighbouringTiles())
@ -76,7 +76,7 @@ int distToNearestNeighbour(BattleHex hex, const std::vector<int> & dists, Battle
return ret; return ret;
} }
bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const std::vector<int> & dists) bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists)
{ {
return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists); return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
} }
@ -97,9 +97,9 @@ static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const Battl
BattleAction CStupidAI::activeStack( const CStack * stack ) BattleAction CStupidAI::activeStack( const CStack * stack )
{ {
//boost::this_thread::sleep(boost::posix_time::seconds(2)); //boost::this_thread::sleep(boost::posix_time::seconds(2));
print("activeStack called"); print("activeStack called for " + stack->nodeName());
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, false); std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, false);
std::vector<int> dists = cb->battleGetDistances(stack); auto dists = cb->battleGetDistances(stack);
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable; std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
if(stack->type->idNumber == 145) //catapult if(stack->type->idNumber == 145) //catapult
@ -246,33 +246,66 @@ void CStupidAI::print(const std::string &text) const
tlog6 << "CStupidAI [" << this <<"]: " << text << std::endl; tlog6 << "CStupidAI [" << this <<"]: " << text << std::endl;
} }
BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex hex) BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
{ {
BattleHex realDest = hex; assert(destination.isValid());
BattleHex predecessors[GameConstants::BFIELD_SIZE]; auto avHexes = cb->battleGetAvailableHexes(stack, false);
std::vector<int> dists = cb->battleGetDistances(stack, hex); auto reachability = cb->getReachability(stack);
if(distToNearestNeighbour(hex, dists, &realDest) > GameConstants::BFIELD_SIZE)
if(vstd::contains(avHexes, destination))
return BattleAction::makeMove(stack, destination);
auto destNeighbours = destination.neighbouringTiles();
if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
{ {
print("goTowards: Cannot reach"); tlog3 << "Warning: already standing on neighbouring tile!" << std::endl;
//We shouldn't even be here...
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
dists = cb->battleGetDistances(stack, realDest, predecessors); vstd::erase_if(destNeighbours, [&](BattleHex hex){ return !reachability.accessibility.accessible(hex, stack); });
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, false);
if(!avHexes.size()) if(!avHexes.size() || !destNeighbours.size()) //we are blocked or dest is blocked
{ {
print("goTowards: Stack cannot move! That's " + stack->nodeName()); print("goTowards: Stack cannot move! That's " + stack->nodeName());
return BattleAction::makeDefend(stack); return BattleAction::makeDefend(stack);
} }
while(1) if(stack->hasBonusOfType(Bonus::FLYING))
{ {
assert(realDest.isValid()); // Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
if(vstd::contains(avHexes, hex)) // We just check all available hexes and pick the one closest to the target.
return BattleAction::makeMove(stack, hex); auto distToDestNeighbour = [&](BattleHex hex) -> int
{
auto nearestNeighbourToHex = vstd::minElementByFun(destNeighbours, [&](BattleHex a)
{
return BattleHex::getDistance(a, hex);
});
hex = predecessors[hex]; return BattleHex::getDistance(*nearestNeighbourToHex, hex);
};
auto nearestAvailableHex = vstd::minElementByFun(avHexes, distToDestNeighbour);
return BattleAction::makeMove(stack, *nearestAvailableHex);
}
else
{
BattleHex bestNeighbor = destination;
if(distToNearestNeighbour(destination, reachability.distances, &bestNeighbor) > GameConstants::BFIELD_SIZE)
{
print("goTowards: Cannot reach");
return BattleAction::makeDefend(stack);
}
BattleHex currentDest = bestNeighbor;
while(1)
{
assert(currentDest.isValid());
if(vstd::contains(avHexes, currentDest))
return BattleAction::makeMove(stack, currentDest);
currentDest = reachability.predecessors[currentDest];
}
} }
} }

View File

@ -5,14 +5,14 @@
class CStupidAI : public CBattleGameInterface class CStupidAI : public CBattleGameInterface
{ {
int side; int side;
CBattleCallback *cb; CPlayerBattleCallback *cb;
void print(const std::string &text) const; void print(const std::string &text) const;
public: public:
CStupidAI(void); CStupidAI(void);
~CStupidAI(void); ~CStupidAI(void);
void init(CBattleCallback * CB) OVERRIDE; void init(CPlayerBattleCallback * CB) OVERRIDE;
void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero
void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero
BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack BattleAction activeStack(const CStack * stack) OVERRIDE; //called when it's turn of that stack

View File

@ -174,13 +174,6 @@ void removeDuplicates(std::vector<T> &vec)
vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
} }
template<typename Range, typename Predicate>
void erase_if(Range &vec, Predicate pred)
{
vec.erase(boost::remove_if(vec, pred),vec.end());
}
struct AtScopeExit struct AtScopeExit
{ {
boost::function<void()> foo; boost::function<void()> foo;
@ -1711,7 +1704,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true)); cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true));
waitTillFree(); //movement may cause battle or blocking dialog waitTillFree(); //movement may cause battle or blocking dialog
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
if(h->tempOwner != playerID) //we lost hero - remove all tasks assigned to him/her if(!h) //we lost hero - remove all tasks assigned to him/her
{ {
lostHero(h); lostHero(h);
//we need to throw, otherwise hero will be assigned to sth again //we need to throw, otherwise hero will be assigned to sth again
@ -1727,7 +1720,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
performObjectInteraction (visitedObject, h); performObjectInteraction (visitedObject, h);
} }
if(h->tempOwner == playerID) //we could have lost hero after last move if(h) //we could have lost hero after last move
{ {
cb->recalculatePaths(); cb->recalculatePaths();
if (startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target if (startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target
@ -3616,7 +3609,7 @@ HeroPtr::HeroPtr(const CGHeroInstance *H)
h = H; h = H;
name = h->name; name = h->name;
hid = H->subID; hid = H->id;
// infosCount[ai->playerID][hid]++; // infosCount[ai->playerID][hid]++;
} }
@ -3641,12 +3634,23 @@ const CGHeroInstance * HeroPtr::get(bool doWeExpectNull /*= false*/) const
{ {
//TODO? check if these all assertions every time we get info about hero affect efficiency //TODO? check if these all assertions every time we get info about hero affect efficiency
// //
//behave terribly when attempting unauthorised access to hero that is not ours (or was lost) //behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
assert(doWeExpectNull || h); assert(doWeExpectNull || h);
if(h) if(h)
{ {
assert(cb->getObj(h->id)); auto obj = cb->getObj(hid);
assert(h->tempOwner == ai->playerID); const bool owned = obj && obj->tempOwner == ai->playerID;
if(doWeExpectNull && !owned)
{
return nullptr;
}
else
{
assert(obj);
assert(owned);
}
} }
return h; return h;

View File

@ -78,17 +78,15 @@ public:
struct CPack; struct CPack;
class CBattleCallback : public IBattleCallback, public CBattleInfoCallback class CBattleCallback : public IBattleCallback, public CPlayerBattleCallback
{ {
private:
CBattleCallback(CGameState *GS, int Player, CClient *C);
protected: protected:
int sendRequest(const CPack *request); //returns requestID (that'll be matched to requestID in PackageApplied) int sendRequest(const CPack *request); //returns requestID (that'll be matched to requestID in PackageApplied)
CClient *cl; CClient *cl;
//virtual bool hasAccess(int playerId) const; //virtual bool hasAccess(int playerId) const;
public: public:
CBattleCallback(CGameState *GS, int Player, CClient *C);
int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack
bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions

View File

@ -44,17 +44,10 @@
#include <queue> #include <queue>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <string>
//#include <unordered_map>
#include <utility> #include <utility>
#include <numeric> #include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <memory>
#include <cstdlib>
//The only available version is 3, as of Boost 1.50 //The only available version is 3, as of Boost 1.50
#define BOOST_FILESYSTEM_VERSION 3 #define BOOST_FILESYSTEM_VERSION 3
@ -72,8 +65,10 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <boost/range/algorithm.hpp> #include <boost/range/algorithm.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp> #include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp> #include <boost/unordered_map.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
@ -172,7 +167,14 @@ namespace vstd
template <typename Container, typename Item> template <typename Container, typename Item>
bool contains(const Container & c, const Item &i) bool contains(const Container & c, const Item &i)
{ {
return std::find(c.begin(),c.end(),i) != c.end(); return std::find(boost::begin(c), boost::end(c),i) != boost::end(c);
}
//returns true if container c contains item i
template <typename Container, typename Pred>
bool contains_if(const Container & c, Pred p)
{
return std::find_if(boost::begin(c), boost::end(c), p) != boost::end(c);
} }
//returns true if map c contains item i //returns true if map c contains item i
@ -201,7 +203,7 @@ namespace vstd
int find_pos(const Container & c, const T2 &s) int find_pos(const Container & c, const T2 &s)
{ {
size_t i=0; size_t i=0;
for (auto iter = c.begin(); iter != c.end(); iter++, i++) for (auto iter = boost::begin(c); iter != boost::end(c); iter++, i++)
if(*iter == s) if(*iter == s)
return i; return i;
return -1; return -1;
@ -343,11 +345,39 @@ namespace vstd
{ {
assert(r.size()); assert(r.size());
index %= r.size(); index %= r.size();
// auto itr = std::begin(r); //not available in gcc-4.5 auto itr = boost::begin(r);
auto itr = r.begin();
std::advance(itr, index); std::advance(itr, index);
return *itr; return *itr;
} }
template<typename Range, typename Predicate>
void erase_if(Range &vec, Predicate pred)
{
vec.erase(boost::remove_if(vec, pred),vec.end());
}
template<typename InputRange, typename OutputIterator, typename Predicate>
OutputIterator copy_if(const InputRange &input, OutputIterator result, Predicate pred)
{
return std::copy_if(boost::const_begin(input), boost::end(input), result, pred);
}
template <typename Container>
std::insert_iterator<Container> set_inserter(Container &c)
{
return std::inserter(c, c.end());
}
//Retuns iterator to the element for which the value of ValueFunction is minimal
template<class ForwardRange, class ValueFunction>
auto minElementByFun(const ForwardRange& rng, ValueFunction vf) -> decltype(boost::begin(rng))
{
typedef decltype(*boost::begin(rng)) ElemType;
return boost::min_element(rng, [&] (const ElemType &lhs, const ElemType &rhs) -> bool
{
return vf(lhs) < vf(rhs);
});
}
} }
using std::shared_ptr; using std::shared_ptr;

View File

@ -141,7 +141,7 @@ CAttackAnimation::CAttackAnimation(CBattleInterface *_owner, const CStack *attac
assert(attackingStack && "attackingStack is NULL in CBattleAttack::CBattleAttack !\n"); assert(attackingStack && "attackingStack is NULL in CBattleAttack::CBattleAttack !\n");
bool isCatapultAttack = attackingStack->hasBonusOfType(Bonus::CATAPULT) bool isCatapultAttack = attackingStack->hasBonusOfType(Bonus::CATAPULT)
&& owner->curInt->cb->battleGetWallUnderHex(_dest) >= 0; && owner->curInt->cb->battleHexToWallPart(_dest) >= 0;
assert(attackedStack || isCatapultAttack); assert(attackedStack || isCatapultAttack);
attackingStackPosBeforeReturn = attackingStack->position; attackingStackPosBeforeReturn = attackingStack->position;

View File

@ -1252,16 +1252,35 @@ void CBattleInterface::bSpellf()
CCS->curh->changeGraphic(0,0); CCS->curh->changeGraphic(0,0);
if ( myTurn && curInt->cb->battleCanCastSpell()) if(!myTurn)
return;
auto myHero = currentHero();
ESpellCastProblem::ESpellCastProblem spellCastProblem;
if (curInt->cb->battleCanCastSpell(&spellCastProblem))
{ {
const CGHeroInstance * chi = NULL; CSpellWindow * spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), myHero, curInt);
if(attackingHeroInstance->tempOwner == curInt->playerID)
chi = attackingHeroInstance;
else
chi = defendingHeroInstance;
CSpellWindow * spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), chi, curInt);
GH.pushInt(spellWindow); GH.pushInt(spellWindow);
} }
else if(spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
{
//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
auto blockingBonus = currentHero()->getBonus(Selector::type(Bonus::BLOCK_ALL_MAGIC));
if(!blockingBonus)
return;;
if(blockingBonus->source == Bonus::ARTIFACT)
{
const int artID = blockingBonus->sid;
//If we have artifact, put name of our hero. Otherwise assume it's the enemy.
//TODO check who *really* is source of bonus
std::string heroName = myHero->hasArt(artID) ? myHero->name : enemyHero().name;
//%s wields the %s, an ancient artifact which creates a p dead to all magic.
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
% heroName % CGI->arth->artifacts[artID]->Name()));
}
}
} }
void CBattleInterface::bWaitf() void CBattleInterface::bWaitf()
@ -1495,11 +1514,11 @@ bool CBattleInterface::isCatapultAttackable(BattleHex hex) const
if(!siegeH || tacticsMode) if(!siegeH || tacticsMode)
return false; return false;
int wallUnder = curInt->cb->battleGetWallUnderHex(hex); int wallUnder = curInt->cb->battleHexToWallPart(hex);
if(wallUnder == -1) if(wallUnder == -1)
return false; return false;
return curInt->cb->battleGetWallState(wallUnder) < 3; return curInt->cb->battleGetWallState(wallUnder) < EWallState::DESTROYED;
} }
const CGHeroInstance * CBattleInterface::getActiveHero() const CGHeroInstance * CBattleInterface::getActiveHero()
@ -1985,7 +2004,9 @@ void CBattleInterface::activateStack()
bWait->block(vstd::contains(s->state, EBattleStackState::WAITING)); //block waiting button if stack has been already waiting bWait->block(vstd::contains(s->state, EBattleStackState::WAITING)); //block waiting button if stack has been already waiting
//block cast spell button if hero doesn't have a spellbook //block cast spell button if hero doesn't have a spellbook
bSpell->block(!curInt->cb->battleCanCastSpell()); ESpellCastProblem::ESpellCastProblem spellcastingProblem;
bool canCastSpells = curInt->cb->battleCanCastSpell(&spellcastingProblem);
bSpell->block(!canCastSpells && spellcastingProblem != ESpellCastProblem::MAGIC_IS_BLOCKED); //if magic is blocked, we leave button active, so the message can be displayed (cf bug #97)
bSurrender->block((curInt == attackerInt ? defendingHeroInstance : attackingHeroInstance) == NULL); bSurrender->block((curInt == attackerInt ? defendingHeroInstance : attackingHeroInstance) == NULL);
bFlee->block(!curInt->cb->battleCanFlee()); bFlee->block(!curInt->cb->battleCanFlee());
bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0); bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0);
@ -3484,6 +3505,25 @@ Point CBattleInterface::whereToBlitObstacleImage(SDL_Surface *image, const CObst
return r.topLeft(); return r.topLeft();
} }
const CGHeroInstance * CBattleInterface::currentHero() const
{
if(attackingHeroInstance->tempOwner == curInt->playerID)
return attackingHeroInstance;
else
return defendingHeroInstance;
}
InfoAboutHero CBattleInterface::enemyHero() const
{
InfoAboutHero ret;
if(attackingHeroInstance->tempOwner == curInt->playerID)
curInt->cb->getHeroInfo(defendingHeroInstance, ret);
else
curInt->cb->getHeroInfo(attackingHeroInstance, ret);
return ret;
}
std::string CBattleInterface::SiegeHelper::townTypeInfixes[GameConstants::F_NUMBER] = {"CS", "RM", "TW", "IN", "NC", "DN", "ST", "FR", "EL"}; std::string CBattleInterface::SiegeHelper::townTypeInfixes[GameConstants::F_NUMBER] = {"CS", "RM", "TW", "IN", "NC", "DN", "ST", "FR", "EL"};
CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner) CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner)

View File

@ -45,6 +45,7 @@ class CCreatureAnimation;
struct ProjectileInfo; struct ProjectileInfo;
class CClickableHex; class CClickableHex;
struct BattleHex; struct BattleHex;
struct InfoAboutHero;
/// Class which manages the locked hex fields that are blocked e.g. by obstacles /// Class which manages the locked hex fields that are blocked e.g. by obstacles
class CBattleObstacle class CBattleObstacle
@ -284,6 +285,9 @@ public:
BattleHex fromWhichHexAttack(BattleHex myNumber); BattleHex fromWhichHexAttack(BattleHex myNumber);
void obstaclePlaced(const CObstacleInstance & oi); void obstaclePlaced(const CObstacleInstance & oi);
const CGHeroInstance * currentHero() const;
InfoAboutHero enemyHero() const;
friend class CPlayerInterface; friend class CPlayerInterface;
friend class CAdventureMapButton; friend class CAdventureMapButton;
friend class CInGameConsole; friend class CInGameConsole;

View File

@ -616,7 +616,7 @@ void CClickableHex::clickRight(tribool down, bool previousState)
void CStackQueue::update() void CStackQueue::update()
{ {
stacksSorted.clear(); stacksSorted.clear();
owner->curInt->cb->getStackQueue(stacksSorted, QUEUE_SIZE); owner->curInt->cb->battleGetStackQueue(stacksSorted, QUEUE_SIZE);
for (int i = 0; i < QUEUE_SIZE ; i++) for (int i = 0; i < QUEUE_SIZE ; i++)
{ {
stackBoxes[i]->setStack(stacksSorted[i]); stackBoxes[i]->setStack(stacksSorted[i]);

View File

@ -892,7 +892,7 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
if (!i->isSecondary()) //display projectile only for primary target if (!i->isSecondary()) //display projectile only for primary target
{ {
const CStack * attacked = cb->battleGetStackByID(i->stackAttacked); const CStack * attacked = cb->battleGetStackByID(i->stackAttacked);
battleInt->stackAttacking(attacker, cb->battleGetPos(i->stackAttacked), attacked, true); battleInt->stackAttacking(attacker, attacked->position, attacked, true);
} }
} }
} }

View File

@ -654,9 +654,24 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
break; break;
case ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED: case ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED:
{ {
std::string text = CGI->generaltexth->allTexts[541], caster = owner->myHero->name; //Recanter's Cloak or similar effect. Try to retrieve bonus
text = boost::str(boost::format(text) % caster); const Bonus *b = owner->myHero->getBonus(Selector::type(Bonus::BLOCK_MAGIC_ABOVE));
owner->myInt->showInfoDialog(text); //TODO what about other values and non-artifact sources?
if(b && b->val == 2 && b->source == Bonus::ARTIFACT)
{
std::string artName = CGI->arth->artifacts[b->sid]->Name();
//The %s prevents %s from casting 3rd level or higher spells.
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536])
% artName % owner->myHero->name));
}
else
{
// General message:
// %s recites the incantations but they seem to have no effect.
std::string text = CGI->generaltexth->allTexts[541], caster = owner->myHero->name;
text = boost::str(boost::format(text) % caster);
owner->myInt->showInfoDialog(text);
}
} }
break; break;
case ESpellCastProblem::NO_APPROPRIATE_TARGET: case ESpellCastProblem::NO_APPROPRIATE_TARGET:

View File

@ -117,7 +117,7 @@ void CClient::waitForMoveAndSend(int color)
try try
{ {
assert(vstd::contains(battleints, color)); assert(vstd::contains(battleints, color));
BattleAction ba = battleints[color]->activeStack(gs->curB->getStack(gs->curB->activeStack, false)); BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
MakeAction temp_action(ba); MakeAction temp_action(ba);
sendRequest(&temp_action, color); sendRequest(&temp_action, color);
return; return;
@ -381,13 +381,14 @@ void CClient::newGame( CConnection *con, StartInfo *si )
battleints[color] = playerint[color]; battleints[color] = playerint[color];
playerint[color]->init(cb.get()); playerint[color]->init(cb.get());
callbacks[color] = cb; battleCallbacks[color] = callbacks[color] = cb;
} }
else else
{ {
CBattleCallback * cbc = new CBattleCallback(gs, color, this); auto cbc = make_shared<CBattleCallback>(gs, color, this);
battleCallbacks[color] = cbc;
battleints[color] = CDynLibHandler::getNewBattleAI("StupidAI"); battleints[color] = CDynLibHandler::getNewBattleAI("StupidAI");
battleints[color]->init(cbc); battleints[color]->init(cbc.get());
} }
} }
@ -399,7 +400,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
battleints[254] = playerint[254] = p; battleints[254] = playerint[254] = p;
privilagedBattleEventReceivers.push_back(p); privilagedBattleEventReceivers.push_back(p);
GH.curInt = p; GH.curInt = p;
p->init(new CCallback(gs, -1, this)); auto cb = make_shared<CCallback>(gs, -1, this);
battleCallbacks[-1] = callbacks[-1] = cb;
p->init(cb.get());
battleStarted(gs->curB); battleStarted(gs->curB);
} }
else else
@ -554,6 +557,15 @@ void CClient::stopConnection()
void CClient::battleStarted(const BattleInfo * info) void CClient::battleStarted(const BattleInfo * info)
{ {
BOOST_FOREACH(auto &battleCb, battleCallbacks)
{
if(vstd::contains(info->sides, battleCb.first) || battleCb.first >= GameConstants::PLAYER_LIMIT)
battleCb.second->setBattle(info);
}
// BOOST_FOREACH(ui8 side, info->sides)
// if(battleCallbacks.count(side))
// battleCallbacks[side]->setBattle(info);
CPlayerInterface * att, * def; CPlayerInterface * att, * def;
if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human) if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human)
att = static_cast<CPlayerInterface*>( playerint[info->sides[0]] ); att = static_cast<CPlayerInterface*>( playerint[info->sides[0]] );
@ -586,10 +598,19 @@ void CClient::battleStarted(const BattleInfo * info)
} }
} }
void CClient::battleFinished()
{
BOOST_FOREACH(ui8 side, gs->curB->sides)
if(battleCallbacks.count(side))
battleCallbacks[side]->setBattle(nullptr);
}
void CClient::loadNeutralBattleAI() void CClient::loadNeutralBattleAI()
{ {
battleints[255] = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String()); battleints[255] = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
battleints[255]->init(new CBattleCallback(gs, 255, this)); auto cbc = make_shared<CBattleCallback>(gs, 255, this);
battleCallbacks[255] = cbc;
battleints[255]->init(cbc.get());
} }
void CClient::commitPackage( CPackForClient *pack ) void CClient::commitPackage( CPackForClient *pack )

View File

@ -114,6 +114,7 @@ class CClient : public IGameCallback
public: public:
CCallback *cb; CCallback *cb;
std::map<ui8,shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces std::map<ui8,shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces
std::map<ui8,shared_ptr<CBattleCallback> > battleCallbacks; //callbacks given to player interfaces
std::vector<IGameEventsReceiver*> privilagedGameEventReceivers; //scripting modules, spectator interfaces std::vector<IGameEventsReceiver*> privilagedGameEventReceivers; //scripting modules, spectator interfaces
std::vector<IBattleEventsReceiver*> privilagedBattleEventReceivers; //scripting modules, spectator interfaces std::vector<IBattleEventsReceiver*> privilagedBattleEventReceivers; //scripting modules, spectator interfaces
std::map<ui8,CGameInterface *> playerint; std::map<ui8,CGameInterface *> playerint;
@ -225,4 +226,5 @@ public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template <typename Handler> void serialize(Handler &h, const int version); template <typename Handler> void serialize(Handler &h, const int version);
void battleFinished();
}; };

View File

@ -589,7 +589,7 @@ void BattleNextRound::applyCl( CClient *cl )
void BattleSetActiveStack::applyCl( CClient *cl ) void BattleSetActiveStack::applyCl( CClient *cl )
{ {
CStack * activated = GS(cl)->curB->getStack(stack); const CStack * activated = GS(cl)->curB->battleGetStackByID(stack);
int playerToCall = -1; //player that will move activated stack int playerToCall = -1; //player that will move activated stack
if( activated->hasBonusOfType(Bonus::HYPNOTIZED) ) if( activated->hasBonusOfType(Bonus::HYPNOTIZED) )
{ {
@ -616,11 +616,12 @@ void BattleObstaclePlaced::applyCl(CClient * cl)
void BattleResult::applyFirstCl( CClient *cl ) void BattleResult::applyFirstCl( CClient *cl )
{ {
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this); BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this);
cl->battleFinished();
} }
void BattleStackMoved::applyFirstCl( CClient *cl ) void BattleStackMoved::applyFirstCl( CClient *cl )
{ {
const CStack * movedStack = GS(cl)->curB->getStack(stack); const CStack * movedStack = GS(cl)->curB->battleGetStackByID(stack);
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStackMoved,movedStack,tilesToMove,distance); BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStackMoved,movedStack,tilesToMove,distance);
} }

View File

@ -248,7 +248,6 @@
<ClCompile Include="CPlayerInterface.cpp" /> <ClCompile Include="CPlayerInterface.cpp" />
<ClCompile Include="CPreGame.cpp" /> <ClCompile Include="CPreGame.cpp" />
<ClCompile Include="CQuestLog.cpp" /> <ClCompile Include="CQuestLog.cpp" />
<ClCompile Include="CSndHandler.cpp" />
<ClCompile Include="CSpellWindow.cpp" /> <ClCompile Include="CSpellWindow.cpp" />
<ClCompile Include="CVideoHandler.cpp" /> <ClCompile Include="CVideoHandler.cpp" />
<ClCompile Include="Graphics.cpp" /> <ClCompile Include="Graphics.cpp" />
@ -289,12 +288,10 @@
<ClInclude Include="CKingdomInterface.h" /> <ClInclude Include="CKingdomInterface.h" />
<ClInclude Include="Client.h" /> <ClInclude Include="Client.h" />
<ClInclude Include="CMessage.h" /> <ClInclude Include="CMessage.h" />
<ClInclude Include="CMusicBase.h" />
<ClInclude Include="CMusicHandler.h" /> <ClInclude Include="CMusicHandler.h" />
<ClInclude Include="CPlayerInterface.h" /> <ClInclude Include="CPlayerInterface.h" />
<ClInclude Include="CPreGame.h" /> <ClInclude Include="CPreGame.h" />
<ClInclude Include="CQuestLog.h" /> <ClInclude Include="CQuestLog.h" />
<ClInclude Include="CSndHandler.h" />
<ClInclude Include="CSoundBase.h" /> <ClInclude Include="CSoundBase.h" />
<ClInclude Include="CSpellWindow.h" /> <ClInclude Include="CSpellWindow.h" />
<ClInclude Include="CVideoHandler.h" /> <ClInclude Include="CVideoHandler.h" />
@ -315,7 +312,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\ChangeLog" /> <None Include="..\ChangeLog" />
<None Include="ClassDiagram21.cd" />
<None Include="vcmi.ico" /> <None Include="vcmi.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -46,28 +46,23 @@
<ClInclude Include="BattleInterface\CBattleInterfaceClasses.h" /> <ClInclude Include="BattleInterface\CBattleInterfaceClasses.h" />
<ClInclude Include="BattleInterface\CCreatureAnimation.h" /> <ClInclude Include="BattleInterface\CCreatureAnimation.h" />
<ClInclude Include="CAdvmapInterface.h" /> <ClInclude Include="CAdvmapInterface.h" />
<ClInclude Include="..\hch\CArtHandler.h" />
<ClInclude Include="CAnimation.h" /> <ClInclude Include="CAnimation.h" />
<ClInclude Include="CBitmapHandler.h" /> <ClInclude Include="CBitmapHandler.h" />
<ClInclude Include="..\hch\CBuildingHandler.h" />
<ClInclude Include="..\CCallback.h" /> <ClInclude Include="..\CCallback.h" />
<ClInclude Include="CCastleInterface.h" /> <ClInclude Include="CCastleInterface.h" />
<ClInclude Include="CConfigHandler.h" /> <ClInclude Include="CConfigHandler.h" />
<ClInclude Include="CCreatureWindow.h" /> <ClInclude Include="CCreatureWindow.h" />
<ClInclude Include="CDefHandler.h" /> <ClInclude Include="CDefHandler.h" />
<ClInclude Include="CGameInfo.h" /> <ClInclude Include="CGameInfo.h" />
<ClInclude Include="..\hch\CHeroHandler.h" />
<ClInclude Include="CHeroWindow.h" /> <ClInclude Include="CHeroWindow.h" />
<ClInclude Include="CKingdomInterface.h" /> <ClInclude Include="CKingdomInterface.h" />
<ClInclude Include="Client.h" /> <ClInclude Include="Client.h" />
<ClInclude Include="CMessage.h" /> <ClInclude Include="CMessage.h" />
<ClInclude Include="..\hch\CObjectHandler.h" />
<ClInclude Include="CMusicHandler.h" /> <ClInclude Include="CMusicHandler.h" />
<ClInclude Include="CPlayerInterface.h" /> <ClInclude Include="CPlayerInterface.h" />
<ClInclude Include="CPreGame.h" /> <ClInclude Include="CPreGame.h" />
<ClInclude Include="CSoundBase.h" /> <ClInclude Include="CSoundBase.h" />
<ClInclude Include="CSpellWindow.h" /> <ClInclude Include="CSpellWindow.h" />
<ClInclude Include="..\hch\CVideoHandler.h" />
<ClInclude Include="CVideoHandler.h" /> <ClInclude Include="CVideoHandler.h" />
<ClInclude Include="FontBase.h" /> <ClInclude Include="FontBase.h" />
<ClInclude Include="FunctionList.h" /> <ClInclude Include="FunctionList.h" />
@ -86,6 +81,7 @@
<ClCompile Include="CQuestLog.cpp"> <ClCompile Include="CQuestLog.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="mapHandler.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="VCMI_client.rc" /> <ResourceCompile Include="VCMI_client.rc" />
@ -94,6 +90,7 @@
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\ChangeLog" />
<None Include="vcmi.ico" /> <None Include="vcmi.ico" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -84,11 +84,10 @@ char BattleHex::getDistance(BattleHex hex1, BattleHex hex2)
return std::max(xDst, yDst) + std::min(xDst, yDst) - (yDst + (yDst + xDst < 2 ? 0 : 1))/2; return std::max(xDst, yDst) + std::min(xDst, yDst) - (yDst + (yDst + xDst < 2 ? 0 : 1))/2;
} }
void BattleHex::checkAndPush(int tile, std::vector<BattleHex> & ret) void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)
{ {
if( tile>=0 && tile<GameConstants::BFIELD_SIZE && (tile%GameConstants::BFIELD_WIDTH != (GameConstants::BFIELD_WIDTH - 1)) if(tile.isAvailable())
&& (tile%GameConstants::BFIELD_WIDTH != 0) ) ret.push_back(tile);
ret.push_back(BattleHex(tile));
} }
bool BattleHex::isAvailable() const bool BattleHex::isAvailable() const

View File

@ -108,7 +108,7 @@ struct DLL_LINKAGE BattleHex
{ {
h & hex; h & hex;
} }
static void checkAndPush(int tile, std::vector<BattleHex> & ret); static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
bool isAvailable() const; //valid position not in first or last column bool isAvailable() const; //valid position not in first or last column
}; };

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
#include "CObstacleInstance.h" #include "CObstacleInstance.h"
#include "ConstTransitivePtr.h" #include "ConstTransitivePtr.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "CBattleCallback.h"
/* /*
* BattleState.h, part of VCMI engine * BattleState.h, part of VCMI engine
@ -28,10 +29,11 @@ class CStackInstance;
struct BattleStackAttacked; struct BattleStackAttacked;
//only for use in BattleInfo //only for use in BattleInfo
struct DLL_LINKAGE SiegeInfo struct DLL_LINKAGE SiegeInfo
{ {
ui8 wallState[8]; //[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; 1 - intact, 2 - damaged, 3 - destroyed ui8 wallState[EWallParts::PARTS_COUNT];
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -39,17 +41,9 @@ struct DLL_LINKAGE SiegeInfo
} }
}; };
struct DLL_LINKAGE AttackableTiles
{
std::set<BattleHex> hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h, const int version)
{
h & hostileCreaturePositions & friendlyCreaturePositions;
}
};
struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback
{ {
ui8 sides[2]; //sides[0] - attacker, sides[1] - defender ui8 sides[2]; //sides[0] - attacker, sides[1] - defender
si32 round, activeStack, selectedStack; si32 round, activeStack, selectedStack;
@ -81,82 +75,62 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
h & static_cast<CBonusSystemNode&>(*this); h & static_cast<CBonusSystemNode&>(*this);
} }
//////////////////////////////////////////////////////////////////////////
BattleInfo();
~BattleInfo(){};
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const; //void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
const CStack *getStackIf(boost::function<bool(const CStack*)> pred) const;
const CStack * getNextStack() const; //which stack will have turn after current one
void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
CStack * getStack(int stackID, bool onlyAlive = true);
const CStack * getStack(int stackID, bool onlyAlive = true) const;
CStack * getStackT(BattleHex tileID, bool onlyAlive = true); CStack * getStackT(BattleHex tileID, bool onlyAlive = true);
const CStack * getStackT(BattleHex tileID, bool onlyAlive = true) const; CStack * getStack(int stackID, bool onlyAlive = true);
void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS const CStack * getNextStack() const; //which stack will have turn after current one
//void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
//void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
//static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
BattleHex getClosestTile (bool attackerOwned, int initialPos, std::set<BattleHex> & possibilities) const; //TODO: vector or set? copying one to another is bad BattleHex getClosestTile (bool attackerOwned, int initialPos, std::set<BattleHex> & possibilities) const; //TODO: vector or set? copying one to another is bad
int getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects int getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects
void makeBFS(BattleHex start, bool*accessibility, BattleHex *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(BattleHex start, bool*accessibility, BattleHex *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<BattleHex>, int > getPath(BattleHex start, BattleHex 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::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const CStack *stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
std::vector<BattleHex> getAccessibility(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable = NULL, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range) //std::vector<BattleHex> getAccessibility(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable = NULL, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range)
bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const; //bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const;
shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const; shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
bool isStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack
std::set<BattleHex> getStoppers(bool whichSidePerspective) const; std::set<BattleHex> getStoppers(bool whichSidePerspective) const;
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting) ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting)
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, TQuantity defenderCount, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) 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, bool deathBlow, bool ballistaDoubleDmg) 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) 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, BattleHex destinationTile); //calculates stack affected by given spell using CBattleInfoCallback::getAttackedCreatures;
void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee std::set<const CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell
std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks //std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
std::set<CStack*> getAdjacentCreatures (const CStack * stack) const; //std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower); static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, int slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack
ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell //std::pair<const CStack *, BattleHex> 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)
int hexToWallPart(BattleHex 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 *, BattleHex> 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 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 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 CStack * sacrificedStack = NULL) const; ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = NULL) const;
ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused
bool resurrects(TSpell spellid) const; //TODO: move it to spellHandler? bool resurrects(TSpell spellid) const; //TODO: move it to spellHandler?
si8 hasDistancePenalty(const CStack * stackID, BattleHex 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, BattleHex destHex) const; //determines if given stack has wall penalty shooting given pos
si8 canTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel) const; //determines if given stack can teleport to given place
bool battleCanShoot(const CStack * stack, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination
const CGHeroInstance * getHero(int player) const; //returns fighting hero that belongs to given player const CGHeroInstance * getHero(int player) const; //returns fighting hero that belongs to given player
ESpellCastProblem::ESpellCastProblem battleCanCastSpell(int player, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell
ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
bool battleTestElementalImmunity(const CStack * subject, const CSpell * spell, Bonus::BonusType element, bool damageSpell) const;
TSpell getRandomBeneficialSpell(const CStack * subject) const;
TSpell getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<CStack*> affectedCreatures, int casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const; std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::set<const CStack*> affectedCreatures, int casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const;
bool battleCanFlee(int player) const; //returns true if player can flee from the battle
const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile
const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none const CGHeroInstance * battleGetOwner(const CStack * stack) const; //returns hero that owns given stack; NULL if none
si8 battleMinSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
void localInit(); void localInit();
void localInitStack(CStack * s); void localInitStack(CStack * s);
static BattleInfo * setupBattle( int3 tile, int terrain, int battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ); static BattleInfo * setupBattle( int3 tile, int terrain, int battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
bool isInTacticRange( BattleHex dest ) const; //bool hasNativeStack(ui8 side) const;
int getSurrenderingCost(int player) const;
bool hasNativeStack(ui8 side) const;
int theOtherPlayer(int player) const; int theOtherPlayer(int player) const;
ui8 whatSide(int player) const; ui8 whatSide(int player) const;
@ -168,7 +142,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor
{ {
public: public:
const CStackInstance *base; const CStackInstance *base; //garrison slot from which stack originates (NULL for war machines, summoned cres, etc)
ui32 ID; //unique ID of stack ui32 ID; //unique ID of stack
ui32 baseAmount; ui32 baseAmount;
@ -226,7 +200,10 @@ public:
bool doubleWide() const; bool doubleWide() const;
BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1
std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front
std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front
bool coversPos(BattleHex position) const; //checks also if unit is double-wide bool coversPos(BattleHex position) const; //checks also if unit is double-wide
std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size

View File

@ -526,31 +526,46 @@ void CArtHandler::getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &ou
out.push_back(art); out.push_back(art);
} }
} }
void CArtHandler::giveArtBonus( int aid, Bonus::BonusType type, int val, int subtype, int valType, ILimiter * limiter, int additionalInfo)
Bonus *createBonus(Bonus::BonusType type, int val, int subtype, int valType, shared_ptr<ILimiter> limiter = nullptr, int additionalInfo = 0)
{ {
Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype); Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);
added->additionalInfo = additionalInfo; added->additionalInfo = additionalInfo;
added->valType = valType; added->valType = valType;
added->limiter.reset(limiter); added->limiter = limiter;
if(type == Bonus::MORALE || type == Bonus::LUCK) return added;
added->description = artifacts[aid]->Name() + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
else
added->description = artifacts[aid]->Name();
artifacts[aid]->addNewBonus(added);
} }
void CArtHandler::giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, IPropagator* propagator /*= NULL*/, int additionalInfo) Bonus *createBonus(Bonus::BonusType type, int val, int subtype, shared_ptr<IPropagator> propagator = nullptr, int additionalInfo = 0)
{ {
Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,aid,subtype); Bonus *added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);
added->additionalInfo = additionalInfo; added->additionalInfo = additionalInfo;
added->valType = Bonus::BASE_NUMBER; added->valType = Bonus::BASE_NUMBER;
added->propagator.reset(propagator); added->propagator = propagator;
if(type == Bonus::MORALE || type == Bonus::LUCK) return added;
added->description = artifacts[aid]->Name() + (val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(val);
else
added->description = artifacts[aid]->Name();
artifacts[aid]->addNewBonus(added);
} }
void CArtHandler::giveArtBonus( int aid, Bonus::BonusType type, int val, int subtype, int valType, shared_ptr<ILimiter> limiter, int additionalInfo)
{
giveArtBonus(aid, createBonus(type, val, subtype, valType, limiter, additionalInfo));
}
void CArtHandler::giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, shared_ptr<IPropagator> propagator /*= NULL*/, int additionalInfo)
{
giveArtBonus(aid, createBonus(type, val, subtype, propagator, additionalInfo));
}
void CArtHandler::giveArtBonus(int aid, Bonus *bonus)
{
bonus->sid = aid;
if(bonus->subtype == Bonus::MORALE || bonus->type == Bonus::LUCK)
bonus->description = artifacts[aid]->Name() + (bonus->val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(bonus->val);
else
bonus->description = artifacts[aid]->Name();
artifacts[aid]->addNewBonus(bonus);
}
void CArtHandler::makeItCreatureArt (int aid, bool onlyCreature /*=true*/) void CArtHandler::makeItCreatureArt (int aid, bool onlyCreature /*=true*/)
{ {
CArtifact *a = artifacts[aid]; CArtifact *a = artifacts[aid];
@ -584,6 +599,13 @@ void CArtHandler::addBonuses()
#define ART_ATTACK_AND_DEFENSE(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val) #define ART_ATTACK_AND_DEFENSE(ID, val) ART_PRIM_SKILL(ID,0,val); ART_PRIM_SKILL(ID,1,val)
#define ART_POWER_AND_KNOWLEDGE(ID, val) ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val) #define ART_POWER_AND_KNOWLEDGE(ID, val) ART_PRIM_SKILL(ID,2,val); ART_PRIM_SKILL(ID,3,val)
//Propagators/limiters used more than once
auto battleWidePropagator = make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE);
auto visitedTownPropagator = make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR);
auto shooterOnlyLimiter = make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER);
auto dragonNatureLimiter = make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE);
//Attack bonus artifacts (Weapons) //Attack bonus artifacts (Weapons)
ART_PRIM_SKILL(7,0,+2); //Centaur Axe ART_PRIM_SKILL(7,0,+2); //Centaur Axe
ART_PRIM_SKILL(8,0,+3); //Blackshard of the Dead Knight ART_PRIM_SKILL(8,0,+3); //Blackshard of the Dead Knight
@ -697,7 +719,7 @@ void CArtHandler::addBonuses()
giveArtBonus(81,Bonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire giveArtBonus(81,Bonus::FIRE_SPELL_DMG_PREMY,+50);//Orb of Tempestuous Fire
giveArtBonus(82,Bonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain giveArtBonus(82,Bonus::WATER_SPELL_DMG_PREMY,+50);//Orb of Driving Rain
giveArtBonus(83,Bonus::LEVEL_SPELL_IMMUNITY,3,-1,Bonus::INDEPENDENT_MAX);//Recanter's Cloak giveArtBonus(83,createBonus(Bonus::BLOCK_MAGIC_ABOVE, 2, -1, Bonus::INDEPENDENT_MIN)->addPropagator(battleWidePropagator));//Recanter's Cloak
giveArtBonus(84,Bonus::BLOCK_MORALE,0);//Spirit of Oppression giveArtBonus(84,Bonus::BLOCK_MORALE,0);//Spirit of Oppression
giveArtBonus(85,Bonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour giveArtBonus(85,Bonus::BLOCK_LUCK,0);//Hourglass of the Evil Hour
@ -707,8 +729,8 @@ void CArtHandler::addBonuses()
giveArtBonus(89,Bonus::EARTH_SPELLS,0);//Tome of Earth Magic giveArtBonus(89,Bonus::EARTH_SPELLS,0);//Tome of Earth Magic
giveArtBonus(90,Bonus::WATER_WALKING, 0, 1);//Boots of Levitation giveArtBonus(90,Bonus::WATER_WALKING, 0, 1);//Boots of Levitation
giveArtBonus(91,Bonus::NO_DISTANCE_PENALTY,0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER));//Golden Bow giveArtBonus(91,Bonus::NO_DISTANCE_PENALTY,0, 0, 0, shooterOnlyLimiter);//Golden Bow
giveArtBonus(91,Bonus::NO_WALL_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(91,Bonus::NO_WALL_PENALTY, 0, 0, 0, shooterOnlyLimiter);
giveArtBonus(92,Bonus::SPELL_IMMUNITY,0,35);//Sphere of Permanence giveArtBonus(92,Bonus::SPELL_IMMUNITY,0,35);//Sphere of Permanence
giveArtBonus(93,Bonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability giveArtBonus(93,Bonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability
@ -720,14 +742,15 @@ void CArtHandler::addBonuses()
giveArtBonus(98,Bonus::LAND_MOVEMENT,+600);//Boots of Speed giveArtBonus(98,Bonus::LAND_MOVEMENT,+600);//Boots of Speed
giveArtBonus(99,Bonus::STACKS_SPEED,+2);//Cape of Velocity giveArtBonus(99,Bonus::STACKS_SPEED,+2);//Cape of Velocity
giveArtBonus(100,Bonus::SPELL_IMMUNITY,0,59);//Pendant of Dispassion giveArtBonus(100,Bonus::SPELL_IMMUNITY,0,Spells::BERSERK);//Pendant of Dispassion
giveArtBonus(101,Bonus::SPELL_IMMUNITY,0,62);//Pendant of Second Sight giveArtBonus(101,Bonus::SPELL_IMMUNITY,0,Spells::BLIND);//Pendant of Second Sight
giveArtBonus(102,Bonus::SPELL_IMMUNITY,0,42);//Pendant of Holiness giveArtBonus(102,Bonus::SPELL_IMMUNITY,0,Spells::CURSE);//Pendant of Holiness
giveArtBonus(103,Bonus::SPELL_IMMUNITY,0,24);//Pendant of Life giveArtBonus(103,Bonus::SPELL_IMMUNITY,0,Spells::DEATH_RIPPLE);//Pendant of Life
giveArtBonus(104,Bonus::SPELL_IMMUNITY,0,25, 1, new HasAnotherBonusLimiter(Bonus::UNDEAD));//Pendant of Death does not display info for living stacks giveArtBonus(104,Bonus::SPELL_IMMUNITY,0,Spells::DESTROY_UNDEAD, 1, make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD));//Pendant of Death does not display info for living stacks
giveArtBonus(105,Bonus::SPELL_IMMUNITY,0,60);//Pendant of Free Will giveArtBonus(105,Bonus::SPELL_IMMUNITY,0,Spells::HYPNOTIZE);//Pendant of Free Will
giveArtBonus(106,Bonus::SPELL_IMMUNITY,0,17);//Pendant of Negativity giveArtBonus(106,Bonus::SPELL_IMMUNITY,0,Spells::LIGHTNING_BOLT);//Pendant of Negativity
giveArtBonus(107,Bonus::SPELL_IMMUNITY,0,61);//Pendant of Total Recall giveArtBonus(106,Bonus::SPELL_IMMUNITY,0,Spells::CHAIN_LIGHTNING);//Pendant of Negativity
giveArtBonus(107,Bonus::SPELL_IMMUNITY,0,Spells::FORGETFULNESS);//Pendant of Total Recall
giveArtBonus(108,Bonus::MORALE,+3);//Pendant of Courage giveArtBonus(108,Bonus::MORALE,+3);//Pendant of Courage
giveArtBonus(108,Bonus::LUCK,+3);//Pendant of Courage giveArtBonus(108,Bonus::LUCK,+3);//Pendant of Courage
@ -741,11 +764,13 @@ void CArtHandler::addBonuses()
giveArtBonus(116,Bonus::GENERATE_RESOURCE,+750, Res::GOLD); //Endless Bag of Gold giveArtBonus(116,Bonus::GENERATE_RESOURCE,+750, Res::GOLD); //Endless Bag of Gold
giveArtBonus(117,Bonus::GENERATE_RESOURCE,+500, Res::GOLD); //Endless Purse of Gold giveArtBonus(117,Bonus::GENERATE_RESOURCE,+500, Res::GOLD); //Endless Purse of Gold
giveArtBonus(118,Bonus::CREATURE_GROWTH,+5,1, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Legs of Legion
giveArtBonus(119,Bonus::CREATURE_GROWTH,+4,2, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Loins of Legion //Town will receive bonus if hero is visiting town or stays in its garrison.
giveArtBonus(120,Bonus::CREATURE_GROWTH,+3,3, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Torso of Legion giveArtBonus(118,Bonus::CREATURE_GROWTH,+5,1, visitedTownPropagator); //Legs of Legion
giveArtBonus(121,Bonus::CREATURE_GROWTH,+2,4, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Arms of Legion giveArtBonus(119,Bonus::CREATURE_GROWTH,+4,2, visitedTownPropagator); //Loins of Legion
giveArtBonus(122,Bonus::CREATURE_GROWTH,+1,5, new CPropagatorNodeType(CBonusSystemNode::TOWN_AND_VISITOR)); //Head of Legion giveArtBonus(120,Bonus::CREATURE_GROWTH,+3,3, visitedTownPropagator); //Torso of Legion
giveArtBonus(121,Bonus::CREATURE_GROWTH,+2,4, visitedTownPropagator); //Arms of Legion
giveArtBonus(122,Bonus::CREATURE_GROWTH,+1,5, visitedTownPropagator); //Head of Legion
//Sea Captain's Hat //Sea Captain's Hat
giveArtBonus(123,Bonus::WHIRLPOOL_PROTECTION,0); giveArtBonus(123,Bonus::WHIRLPOOL_PROTECTION,0);
@ -753,13 +778,13 @@ void CArtHandler::addBonuses()
giveArtBonus(123,Bonus::SPELL,3,0, Bonus::INDEPENDENT_MAX); giveArtBonus(123,Bonus::SPELL,3,0, Bonus::INDEPENDENT_MAX);
giveArtBonus(123,Bonus::SPELL,3,1, Bonus::INDEPENDENT_MAX); giveArtBonus(123,Bonus::SPELL,3,1, Bonus::INDEPENDENT_MAX);
giveArtBonus(124,Bonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat giveArtBonus(124, Bonus::SPELLS_OF_LEVEL,3,1); //Spellbinder's Hat
giveArtBonus(125,Bonus::ENEMY_CANT_ESCAPE,0); //Shackles of War giveArtBonus(125, Bonus::ENEMY_CANT_ESCAPE,0); //Shackles of War
giveArtBonus(126,Bonus::LEVEL_SPELL_IMMUNITY,GameConstants::SPELL_LEVELS,-1,Bonus::INDEPENDENT_MAX);//Orb of Inhibition giveArtBonus(126, Bonus::BLOCK_ALL_MAGIC, 0, -1, battleWidePropagator);//Orb of Inhibition
//vial of dragon blood //vial of dragon blood
giveArtBonus(127, Bonus::PRIMARY_SKILL, +5, PrimarySkill::ATTACK, Bonus::BASE_NUMBER, new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE)); giveArtBonus(127, Bonus::PRIMARY_SKILL, +5, PrimarySkill::ATTACK, Bonus::BASE_NUMBER, dragonNatureLimiter);
giveArtBonus(127, Bonus::PRIMARY_SKILL, +5, PrimarySkill::DEFENSE, Bonus::BASE_NUMBER, new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE)); giveArtBonus(127, Bonus::PRIMARY_SKILL, +5, PrimarySkill::DEFENSE, Bonus::BASE_NUMBER, dragonNatureLimiter);
//Armageddon's Blade //Armageddon's Blade
giveArtBonus(128, Bonus::SPELL, 3, 26, Bonus::INDEPENDENT_MAX); giveArtBonus(128, Bonus::SPELL, 3, 26, Bonus::INDEPENDENT_MAX);
@ -786,7 +811,7 @@ void CArtHandler::addBonuses()
giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 52); // Misfortune giveArtBonus(132, Bonus::OPENING_BATTLE_SPELL, 50, 52); // Misfortune
// Statue of Legion - gives only 50% growth // Statue of Legion - gives only 50% growth
giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50, -1, new CPropagatorNodeType(CBonusSystemNode::PLAYER)); giveArtBonus(133, Bonus::CREATURE_GROWTH_PERCENT, 50, -1, make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER));
//Power of the Dragon Father //Power of the Dragon Father
giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4, -1, Bonus::INDEPENDENT_MAX); giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4, -1, Bonus::INDEPENDENT_MAX);
@ -798,9 +823,9 @@ void CArtHandler::addBonuses()
giveArtBonus(136, Bonus::FREE_SHIP_BOARDING, 0); giveArtBonus(136, Bonus::FREE_SHIP_BOARDING, 0);
//Bow of the Sharpshooter //Bow of the Sharpshooter
giveArtBonus(137, Bonus::NO_DISTANCE_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(137, Bonus::NO_DISTANCE_PENALTY, 0, 0, 0, shooterOnlyLimiter);
giveArtBonus(137, Bonus::NO_WALL_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(137, Bonus::NO_WALL_PENALTY, 0, 0, 0, shooterOnlyLimiter);
giveArtBonus(137, Bonus::FREE_SHOOTING, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(137, Bonus::FREE_SHOOTING, 0, 0, 0, shooterOnlyLimiter);
//Wizard's Well //Wizard's Well
giveArtBonus(138, Bonus::FULL_MANA_REGENERATION, 0); giveArtBonus(138, Bonus::FULL_MANA_REGENERATION, 0);
@ -844,7 +869,7 @@ void CArtHandler::addBonuses()
giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::BERSERK, NULL, 1); giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::BERSERK, NULL, 1);
giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::POISON, NULL, 1); giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::POISON, NULL, 1);
giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::DISRUPTING_RAY, NULL, 1); giveArtBonus(142, Bonus::SPELL_AFTER_ATTACK, 50, Spells::DISRUPTING_RAY, NULL, 1);
artifacts[142].get()->setDescription ("Tripple shots, tripple attack, casts various spells during attack, attacks have range of Inferno, no distance penalty, catapult"); artifacts[142].get()->setDescription ("Triple shots, triple attack, casts various spells during attack, attacks have range of Inferno, no distance penalty, catapult");
//Monster's Power //Monster's Power
giveArtBonus(143, Bonus::STACK_HEALTH, +100, -1, Bonus::PERCENT_TO_BASE); giveArtBonus(143, Bonus::STACK_HEALTH, +100, -1, Bonus::PERCENT_TO_BASE);
giveArtBonus(143, Bonus::CREATURE_DAMAGE, +100, 2, Bonus::PERCENT_TO_ALL); giveArtBonus(143, Bonus::CREATURE_DAMAGE, +100, 2, Bonus::PERCENT_TO_ALL);

View File

@ -34,6 +34,14 @@ namespace ArtifactPosition
}; };
} }
namespace ArtifactId
{
enum ArtifactId
{
SPELLBOOK = 17
};
}
namespace ArtBearer namespace ArtBearer
{ {
enum enum
@ -181,8 +189,9 @@ public:
class DLL_LINKAGE CArtHandler //handles artifacts class DLL_LINKAGE CArtHandler //handles artifacts
{ {
void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER, ILimiter * limiter = NULL, int additionalinfo = 0); void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype = -1, int valType = Bonus::BASE_NUMBER, shared_ptr<ILimiter> limiter = NULL, int additionalinfo = 0);
void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, IPropagator* propagator, int additionalinfo = 0); void giveArtBonus(int aid, Bonus::BonusType type, int val, int subtype, shared_ptr<IPropagator> propagator, int additionalinfo = 0);
void giveArtBonus(int aid, Bonus *bonus);
public: public:
std::vector<CArtifact*> treasures, minors, majors, relics; std::vector<CArtifact*> treasures, minors, majors, relics;
std::vector< ConstTransitivePtr<CArtifact> > artifacts; std::vector< ConstTransitivePtr<CArtifact> > artifacts;

2021
lib/CBattleCallback.cpp Normal file

File diff suppressed because it is too large Load Diff

269
lib/CBattleCallback.h Normal file
View File

@ -0,0 +1,269 @@
#pragma once
#include "BattleHex.h"
class CGameState;
class CGTownInstance;
class CGHeroInstance;
class CStack;
class CSpell;
struct BattleInfo;
struct CObstacleInstance;
namespace boost
{class shared_mutex;}
namespace BattleSide
{
enum {ATTACKER = 0, DEFENDER = 1};
}
typedef std::vector<const CStack*> TStacks;
class CBattleInfoEssentials;
//Basic class for various callbacks (interfaces called by players to get info about game and so forth)
class DLL_LINKAGE CCallbackBase
{
const BattleInfo *battle; //battle to which the player is engaged, NULL if none or not applicable
const BattleInfo * getBattle() const
{
return battle;
}
protected:
CGameState *gs;
int player; // -1 gives access to all information, otherwise callback provides only information "visible" for player
CCallbackBase(CGameState *GS, int Player)
: gs(GS), player(Player)
{}
CCallbackBase()
: gs(NULL), player(-1)
{}
void setBattle(const BattleInfo *B);
bool duringBattle() const;
public:
boost::shared_mutex &getGsMutex(); //just return a reference to mutex, does not lock nor anything
friend class CBattleInfoEssentials;
};
struct DLL_LINKAGE AttackableTiles
{
std::set<BattleHex> hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h, const int version)
{
h & hostileCreaturePositions & friendlyCreaturePositions;
}
};
//Accessibility is property of hex in battle. It doesn't depend on stack, side's perspective and so on.
namespace EAccessibility
{
enum EAccessibility
{
ACCESSIBLE,
ALIVE_STACK,
OBSTACLE,
DESTRUCTIBLE_WALL,
GATE, //sieges -> gate opens only for defender stacks
UNAVAILABLE, //indestructible wall parts, special battlefields (like boat-to-boat)
SIDE_COLUMN //used for first and last columns of hexes that are unavailable but wat machines can stand there
};
}
typedef std::array<EAccessibility::EAccessibility, GameConstants::BFIELD_SIZE> TAccessibilityArray;
struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray
{
bool occupiable(const CStack *stack, BattleHex tile) const;
bool accessible(BattleHex tile, const CStack *stack) const; //checks for both tiles if stack is double wide
bool accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const; //checks for both tiles if stack is double wide
};
namespace BattlePerspective
{
enum BattlePerspective
{
INVALID = -2,
ALL_KNOWING = -1,
LEFT_SIDE,
RIGHT_SIDE
};
}
// Reachability info is result of BFS calculation. It's dependant on stack (it's owner, whether it's flying),
// startPosition and perpective.
struct DLL_LINKAGE ReachabilityInfo
{
typedef std::array<int, GameConstants::BFIELD_SIZE> TDistances;
typedef std::array<BattleHex, GameConstants::BFIELD_SIZE> TPredecessors;
static const int INFINITE_DIST = 1000000;
struct DLL_LINKAGE Parameters
{
const CStack *stack; //stack for which calculation is mage => not required (kept for debugging mostly), following variables are enough
bool attackerOwned;
bool doubleWide;
bool flying;
BattleHex startPosition; //assumed position of stack
BattlePerspective::BattlePerspective perspective; //some obstacles (eg. quicksands) may be invisible for some side
Parameters();
Parameters(const CStack *Stack);
};
Parameters params;
AccessibilityInfo accessibility;
TDistances distances;
TPredecessors predecessors;
ReachabilityInfo()
{
distances.fill(INFINITE_DIST);
predecessors.fill(BattleHex::INVALID);
}
bool isReachable(BattleHex hex) const
{
return predecessors[hex].isValid();
}
};
class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase
{
protected:
bool battleDoWeKnowAbout(ui8 side) const;
public:
enum EStackOwnership
{
ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY
};
BattlePerspective::BattlePerspective battleGetMySide() const;
ui8 battleTerrainType() const;
int battleGetBattlefieldType() const; // 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
std::vector<shared_ptr<const CObstacleInstance> > battleGetAllObstacles(boost::optional<BattlePerspective::BattlePerspective> perspective = boost::none) const; //returns all obstacles on the battlefield
TStacks battleGetAllStacks() const; //returns all stacks, alive or dead or undead or mechanical :)
bool battleHasNativeStack(ui8 side) const;
ui8 battleGetWallState(int partOfWall) const; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat
const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, NULL instead
const CStack *battleActiveStack() const;
si8 battleTacticDist() const; //returns tactic distance in current tactics phase; 0 if not in tactics phase
si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?)
bool battleCanFlee(int player) const;
bool battleCanSurrender(int player) const;
ui8 playerToSide(int player) const;
ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
bool battleHasHero(ui8 side) const;
int battleCastSpells(ui8 side) const; //how many spells has given side casted
const CGHeroInstance * battleGetFightingHero(ui8 side) const;
//helpers
TStacks battleAliveStacks() const;
TStacks battleAliveStacks(ui8 side) const;
const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID
bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const;
//ESpellCastProblem::ESpellCastProblem battleCanCastSpell(int player, ECastingMode::ECastingMode mode) const; //Checks if player is able to cast spells (at all) at the moment
};
class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials
{
public:
enum ERandomSpell
{
RANDOM_GENIE, RANDOM_AIMED
};
//battle
shared_ptr<const CObstacleInstance> battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos
void battleGetStackQueue(std::vector<const CStack *> &out, const int howMany, const int turn = 0, int lastMoved = -1) const;
void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back
//void getStackQueue( std::vector<const CStack *> &out, int howMany ) const; //returns vector of stack in order of their move sequence
std::vector<BattleHex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable = NULL) const; //returns numbers of hexes reachable by creature with id ID
int battleGetSurrenderCost(int Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible
ReachabilityInfo::TDistances battleGetDistances(const CStack * stack, BattleHex hex = BattleHex::INVALID, BattleHex * predecessors = NULL) const; //returns vector of distances to [dest hex number]
std::set<BattleHex> battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
bool battleCanShoot(const CStack * stack, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination
bool battleIsStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack
std::set<const CStack*> batteAdjacentCreatures (const CStack * stack) const;
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) 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, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
//hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
std::pair<ui32, ui32> battleEstimateDamage(const CStack * attacker, const CStack * defender, std::pair<ui32, ui32> * retaliationDmg = NULL) const; //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>
si8 battleHasDistancePenalty( const CStack * stack, BattleHex destHex ) const;
si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty
EWallParts::EWallParts battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
//*** MAGIC
si8 battleMaxSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
ESpellCastProblem::ESpellCastProblem battleCanCastSpell(int player, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(int player, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(int player, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const; //determines if creature can cast a spell here
si32 battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;
TSpell getRandomBeneficialSpell(const CStack * subject) const;
TSpell getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const;
const CStack * getStackIf(boost::function<bool(const CStack*)> pred) const;
si8 battleHasShootingPenalty(const CStack * stack, BattleHex destHex)
{
return battleHasDistancePenalty(stack, destHex) || battleHasWallPenalty(stack, destHex);
}
si8 battleCanTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel) const; //checks if teleportation of given stack to given position can take place
//convenience methods using the ones above
bool isInTacticRange( BattleHex dest ) const;
si8 battleGetTacticDist() const; //returns tactic distance for calling player or 0 if this player is not in tactic phase (for ALL_KNOWING actual distance for tactic side)
AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const;
std::set<const CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
ReachabilityInfo getReachability(const CStack *stack) const;
ReachabilityInfo getReachability(const ReachabilityInfo::Parameters &params) const;
AccessibilityInfo getAccesibility() const;
std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const;
protected:
ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters params) const;
ReachabilityInfo makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters params) const;
ReachabilityInfo makeBFS(const CStack *stack) const; //uses default parameters -> stack position and owner's perspective
std::set<BattleHex> getStoppers(BattlePerspective::BattlePerspective whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
};
class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback
{
public:
bool battleCanFlee() const; //returns true if caller can flee from the battle
TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true) const; //returns stacks on battlefield
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) const; //determines if given spell can be casted (and returns problem description)
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell, BattleHex destination) const; //if hero can cast spell here
ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination) const; //determines if creature can cast a spell here
int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible
bool battleCanCastSpell(ESpellCastProblem::ESpellCastProblem *outProblem = nullptr) const; //returns true, if caller can cast a spell. If not, if pointer is given via arg, the reason will be written.
};

View File

@ -16,7 +16,7 @@
using namespace boost::logic; using namespace boost::logic;
class CCallback; class CCallback;
class CBattleCallback; class CPlayerBattleCallback;
class ICallback; class ICallback;
class CGlobalAI; class CGlobalAI;
struct Component; struct Component;
@ -61,7 +61,7 @@ public:
std::string dllName; std::string dllName;
virtual ~CBattleGameInterface() {}; virtual ~CBattleGameInterface() {};
virtual void init(CBattleCallback * CB){}; virtual void init(CPlayerBattleCallback * CB){};
//battle call-ins //battle call-ins
virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack
@ -115,7 +115,7 @@ public:
std::string battleAIName; std::string battleAIName;
CBattleGameInterface *battleAI; CBattleGameInterface *battleAI;
CBattleCallback *cbc; CPlayerBattleCallback *cbc;
//battle interface //battle interface
virtual BattleAction activeStack(const CStack * stack); virtual BattleAction activeStack(const CStack * stack);

View File

@ -256,6 +256,10 @@ bool CSpell::isRisingSpell() const
return vstd::contains(VLC->spellh->risingSpells, id); return vstd::contains(VLC->spellh->risingSpells, id);
} }
bool CSpell::isDamageSpell() const
{
return vstd::contains(VLC->spellh->damageSpells, id);
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos) bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
{ {
int3 diff = pos - center; int3 diff = pos - center;

View File

@ -22,7 +22,7 @@ class DLL_LINKAGE CSpell
public: public:
enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE}; enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1}; enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
ui32 id; TSpell id;
std::string name; std::string name;
std::string abbName; //abbreviated name std::string abbName; //abbreviated name
std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc
@ -48,6 +48,7 @@ public:
bool isPositive() const; bool isPositive() const;
bool isNegative() const; bool isNegative() const;
bool isRisingSpell() const; bool isRisingSpell() const;
bool isDamageSpell() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@ -161,11 +161,11 @@ void CLodArchiveLoader::initSNDArchive(CFileInputStream & fileStream)
SoundEntryBlock sndEntry = sndEntries[i]; SoundEntryBlock sndEntry = sndEntries[i];
ArchiveEntry entry; ArchiveEntry entry;
//for some reason entries in snd have format NAME\0WAV\0\0\0.... //for some reason entries in snd have format NAME\0WAVRUBBISH....
//we need to replace first \0 with dot and trim line //we need to replace first \0 with dot and take the 3 chars with extension (and drop the rest)
entry.name = std::string(sndEntry.filename, 40); entry.name = std::string(sndEntry.filename, 40);
entry.name.resize(entry.name.find_first_of('\0') + 4); //+4 because we take dot and 3-char extension
entry.name[entry.name.find_first_of('\0')] = '.'; entry.name[entry.name.find_first_of('\0')] = '.';
entry.name.resize(entry.name.find_first_of('\0'));
entry.offset = SDL_SwapLE32(sndEntry.offset); entry.offset = SDL_SwapLE32(sndEntry.offset);
entry.realSize = SDL_SwapLE32(sndEntry.size); entry.realSize = SDL_SwapLE32(sndEntry.size);

View File

@ -140,7 +140,9 @@ namespace ESpellCastProblem
OK, NO_HERO_TO_CAST_SPELL, ALREADY_CASTED_THIS_TURN, NO_SPELLBOOK, ANOTHER_ELEMENTAL_SUMMONED, OK, NO_HERO_TO_CAST_SPELL, ALREADY_CASTED_THIS_TURN, NO_SPELLBOOK, ANOTHER_ELEMENTAL_SUMMONED,
HERO_DOESNT_KNOW_SPELL, NOT_ENOUGH_MANA, ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL, HERO_DOESNT_KNOW_SPELL, NOT_ENOUGH_MANA, ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL,
SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL, SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL,
NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET, ONGOING_TACTIC_PHASE NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET, ONGOING_TACTIC_PHASE,
MAGIC_IS_BLOCKED, //For Orb of Inhibition and similar - no casting at all
INVALID
}; };
} }
@ -172,6 +174,28 @@ namespace ECommander
const int MAX_SKILL_LEVEL = 5; const int MAX_SKILL_LEVEL = 5;
} }
namespace EWallParts
{
enum EWallParts
{
INDESTRUCTIBLE_PART = -2, INVALID = -1,
KEEP = 0, BOTTOM_TOWER, BOTTOM_WALL, BELOW_GATE, OVER_GATE, UPPER_WAL, UPPER_TOWER, GATE,
PARTS_COUNT
};
}
namespace EWallState
{
enum
{
NONE, //no wall
INTACT,
DAMAGED,
DESTROYED
};
}
namespace Obj namespace Obj
{ {
enum enum
@ -224,6 +248,17 @@ namespace Obj
}; };
} }
namespace SecSkillLevel
{
enum SecSkillLevel
{
NONE,
BASIC,
ADVANCED,
EXPERT
};
}
//follows ERM BI (battle image) format //follows ERM BI (battle image) format
namespace BattlefieldBI namespace BattlefieldBI
{ {

View File

@ -134,10 +134,26 @@ int BonusList::totalValue() const
if(hasIndepMin && hasIndepMax) if(hasIndepMin && hasIndepMax)
assert(indepMin < indepMax); assert(indepMin < indepMax);
const int notIndepBonuses = boost::count_if(bonuses, [](const Bonus *b)
{
return b->valType != Bonus::INDEPENDENT_MAX && b->valType != Bonus::INDEPENDENT_MIN;
});
if (hasIndepMax) if (hasIndepMax)
vstd::amax(valFirst, indepMax); {
if(notIndepBonuses)
vstd::amax(valFirst, indepMax);
else
valFirst = indepMax;
}
if (hasIndepMin) if (hasIndepMin)
vstd::amin(valFirst, indepMin); {
if(notIndepBonuses)
vstd::amin(valFirst, indepMin);
else
valFirst = indepMin;
}
return valFirst; return valFirst;
} }

View File

@ -95,6 +95,8 @@ typedef boost::function<bool(const Bonus*)> CSelector;
BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \ BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \ BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \ BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
BONUS_NAME(BLOCK_MAGIC_ABOVE) /*blocks casting spells of the level > value */ \
BONUS_NAME(BLOCK_ALL_MAGIC) /*blocks casting spells of the level > value */ \
BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/ \ BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/ \
BONUS_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \ BONUS_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
BONUS_NAME(NO_WALL_PENALTY) \ BONUS_NAME(NO_WALL_PENALTY) \
@ -626,7 +628,8 @@ public:
} }
enum ENodeTypes enum ENodeTypes
{ {
UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM, TOWN_AND_VISITOR UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
TOWN_AND_VISITOR, BATTLE
}; };
}; };

View File

@ -34,417 +34,6 @@
extern boost::rand48 ran; extern boost::rand48 ran;
boost::shared_mutex& CCallbackBase::getGsMutex()
{
return *gs->mx;
}
si8 CBattleInfoCallback::battleHasDistancePenalty( const CStack * stack, BattleHex destHex )
{
return gs->curB->hasDistancePenalty(stack, destHex);
}
si8 CBattleInfoCallback::battleHasWallPenalty( const CStack * stack, BattleHex destHex )
{
return gs->curB->hasWallPenalty(stack, destHex);
}
si8 CBattleInfoCallback::battleCanTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel)
{
return gs->curB->canTeleportTo(stack, destHex, telportLevel);
}
std::vector<int> CBattleInfoCallback::battleGetDistances(const CStack * stack, BattleHex hex /*= BattleHex::INVALID*/, BattleHex * predecessors /*= NULL*/)
{
// FIXME - This method is broken, hex argument is not used. However AI depends on that wrong behaviour.
if(!hex.isValid())
hex = stack->position;
std::vector<int> ret(GameConstants::BFIELD_SIZE, -1); //fill initial ret with -1's
if(!hex.isValid()) //stack has bad position? probably castle turret, return initial values (they can't move)
return ret;
bool ac[GameConstants::BFIELD_SIZE] = {0};
std::set<BattleHex> occupyable;
gs->curB->getAccessibilityMap(ac, stack->doubleWide(), stack->attackerOwned, false, occupyable, stack->hasBonusOfType(Bonus::FLYING), stack);
BattleHex pr[GameConstants::BFIELD_SIZE];
int dist[GameConstants::BFIELD_SIZE];
gs->curB->makeBFS(stack->position, ac, pr, dist, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), false);
for(int i=0; i<GameConstants::BFIELD_SIZE; ++i)
{
if(pr[i] != -1)
ret[i] = dist[i];
}
if(predecessors)
{
memcpy(predecessors, pr, GameConstants::BFIELD_SIZE * sizeof(BattleHex));
}
return ret;
}
std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos /*= BattleHex::INVALID*/)
{
if(!gs->curB)
{
tlog1 << "battleGetAttackedHexes called when there is no battle!\n";
std::set<BattleHex> set;
return set;
}
return gs->curB->getAttackedHexes(attacker, destinationTile, attackerPos);
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( const CSpell * spell )
{
if(!gs->curB)
{
tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
return ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
}
return gs->curB->battleCanCastThisSpell(player, spell, ECastingMode::HERO_CASTING);
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell(const CSpell * spell, BattleHex destination)
{
if(!gs->curB)
{
tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
return ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
}
return gs->curB->battleCanCastThisSpellHere(player, spell, ECastingMode::HERO_CASTING, destination);
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination)
{
if(!gs->curB)
{
tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
return ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
}
return gs->curB->battleCanCastThisSpellHere(player, spell, ECastingMode::CREATURE_ACTIVE_CASTING, destination);
}
si32 CBattleInfoCallback::battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode)
{
switch (mode)
{
case RANDOM_GENIE:
return gs->curB->getRandomBeneficialSpell(stack); //target
break;
case RANDOM_AIMED:
return gs->curB->getRandomCastedSpell(stack); //caster
break;
default:
tlog1 << "Incorrect mode of battleGetRandomSpell (" << mode <<")\n";
return -1;
}
}
si8 CBattleInfoCallback::battleGetTacticDist()
{
if (!gs->curB)
{
tlog1 << "battleGetTacticDist called when no battle!\n";
return 0;
}
if (gs->curB->sides[gs->curB->tacticsSide] == player)
{
return gs->curB->tacticDistance;
}
return 0;
}
ui8 CBattleInfoCallback::battleGetMySide()
{
if (!gs->curB)
{
tlog1 << "battleGetMySide called when no battle!\n";
return 0;
}
return gs->curB->sides[1] == player;
}
int CBattleInfoCallback::battleGetSurrenderCost()
{
if (!gs->curB)
{
tlog1 << "battleGetSurrenderCost called when no battle!\n";
return -1;
}
return gs->curB->getSurrenderingCost(player);
}
int CBattleInfoCallback::battleGetBattlefieldType()
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
//return gs->battleGetBattlefieldType();
if(!gs->curB)
{
tlog2<<"battleGetBattlefieldType called when there is no battle!"<<std::endl;
return -1;
}
return gs->curB->battlefieldType;
}
// int CBattleInfoCallback::battleGetObstaclesAtTile(BattleHex tile) //returns bitfield
// {
// //TODO - write
// return -1;
// }
std::vector<shared_ptr<const CObstacleInstance> > CBattleInfoCallback::battleGetAllObstacles()
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
std::vector<shared_ptr<const CObstacleInstance> > ret;
if(gs->curB)
{
BOOST_FOREACH(auto oi, gs->curB->obstacles)
{
if(player < 0 || gs->curB->isObstacleVisibleForSide(*oi, battleGetMySide()))
ret.push_back(oi);
}
}
return ret;
}
const CStack* CBattleInfoCallback::battleGetStackByID(int ID, bool onlyAlive)
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!gs->curB) return NULL;
return gs->curB->getStack(ID, onlyAlive);
}
const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyAlive)
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
return gs->curB->battleGetStack(pos, onlyAlive);
}
BattleHex CBattleInfoCallback::battleGetPos(int stack)
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!gs->curB)
{
tlog2<<"battleGetPos called when there is no battle!"<<std::endl;
return BattleHex::INVALID;
}
for(size_t g=0; g<gs->curB->stacks.size(); ++g)
{
if(gs->curB->stacks[g]->ID == stack)
return gs->curB->stacks[g]->position;
}
return BattleHex::INVALID;
}
TStacks CBattleInfoCallback::battleGetStacks(EStackOwnership whose /*= MINE_AND_ENEMY*/, bool onlyAlive /*= true*/)
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
TStacks ret;
if(!gs->curB) //there is no battle
{
tlog2<<"battleGetStacks called when there is no battle!"<<std::endl;
return ret;
}
BOOST_FOREACH(const CStack *s, gs->curB->stacks)
{
bool ownerMatches = (whose == MINE_AND_ENEMY) || (whose == ONLY_MINE && s->owner == player) || (whose == ONLY_ENEMY && s->owner != player);
bool alivenessMatches = s->alive() || !onlyAlive;
if(ownerMatches && alivenessMatches)
ret.push_back(s);
}
return ret;
}
void CBattleInfoCallback::getStackQueue( std::vector<const CStack *> &out, int howMany )
{
if(!gs->curB)
{
tlog2 << "battleGetStackQueue called when there is not battle!" << std::endl;
return;
}
gs->curB->getStackQueue(out, howMany);
}
void CBattleInfoCallback::battleGetStackCountOutsideHexes(bool *ac)
{
if(!gs->curB)
{
tlog2<<"battleGetAvailableHexes called when there is no battle!"<<std::endl;
for (int i = 0; i < GameConstants::BFIELD_SIZE; ++i) ac[i] = false;
}
else {
std::set<BattleHex> ignored;
gs->curB->getAccessibilityMap(ac, false /*ignored*/, false, false, ignored, false /*ignored*/, NULL);
}
}
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable)
{
//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<BattleHex>();
}
return gs->curB->getAccessibility(stack, addOccupiable, attackable);
//return gs->battleGetRange(ID);
}
bool CBattleInfoCallback::battleCanShoot(const CStack * stack, BattleHex dest)
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!gs->curB) return false;
return gs->curB->battleCanShoot(stack, dest);
}
bool CBattleInfoCallback::battleCanCastSpell()
{
if(!gs->curB) //there is no battle
return false;
return gs->curB->battleCanCastSpell(player, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK;
}
bool CBattleInfoCallback::battleCanFlee()
{
return gs->curB->battleCanFlee(player);
}
const CGTownInstance *CBattleInfoCallback::battleGetDefendedTown()
{
if(!gs->curB || gs->curB->town == NULL)
return NULL;
return gs->curB->town;
}
ui8 CBattleInfoCallback::battleGetWallState(int partOfWall)
{
if(!gs->curB || gs->curB->siege == 0)
{
return 0;
}
return gs->curB->si.wallState[partOfWall];
}
int CBattleInfoCallback::battleGetWallUnderHex(BattleHex hex)
{
if(!gs->curB || gs->curB->siege == 0)
{
return -1;
}
return gs->curB->hexToWallPart(hex);
}
TDmgRange CBattleInfoCallback::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->sides[0] == attacker->owner)
{
attackerHero = gs->curB->heroes[0];
defenderHero = gs->curB->heroes[1];
}
else
{
attackerHero = gs->curB->heroes[1];
defenderHero = gs->curB->heroes[0];
}
TDmgRange ret = gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, shooting, 0, false, false, 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, 0, false, false, false).*pairElems[!i];
}
}
}
return ret;
}
ui8 CBattleInfoCallback::battleGetSiegeLevel()
{
if(!gs->curB)
return 0;
return gs->curB->siege;
}
const CGHeroInstance * CBattleInfoCallback::battleGetFightingHero(ui8 side) const
{
if(!gs->curB)
return 0;
//TODO: this method should not exist... you shouldn't be able to get info about enemy hero
return gs->curB->heroes[side];
}
shared_ptr<const CObstacleInstance> CBattleInfoCallback::battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking /*= true*/)
{
if(!gs->curB)
return shared_ptr<const CObstacleInstance>();
BOOST_FOREACH(auto &obs, battleGetAllObstacles())
{
if(vstd::contains(obs->getBlockedTiles(), tile)
|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile)))
{
return obs;
}
}
return shared_ptr<const CObstacleInstance>();
}
int CBattleInfoCallback::battleGetMoatDmg()
{
if(!gs->curB || !gs->curB->town)
return 0;
//TODO move to config file
static const int dmgs[] = {70, 70, -1,
90, 70, 90,
70, 90, 70};
if(gs->curB->town->subID < ARRAY_COUNT(dmgs))
return dmgs[gs->curB->town->subID];
return 0;
}
CGameState * CPrivilagedInfoCallback::gameState () CGameState * CPrivilagedInfoCallback::gameState ()
{ {
return gs; return gs;
@ -729,7 +318,7 @@ int CGameInfoCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * ca
ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1); ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1);
//if there is a battle //if there is a battle
if(gs->curB) if(gs->curB)
return gs->curB->getSpellCost(sp, caster); return gs->curB->battleGetSpellCost(sp, caster);
//if there is no battle //if there is no battle
return caster->getSpellCost(sp); return caster->getSpellCost(sp);
@ -784,7 +373,7 @@ bool CGameInfoCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown
//TODO vision support //TODO vision support
if(town->ID == GameConstants::TOWNI_TYPE) if(town->ID == GameConstants::TOWNI_TYPE)
dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed); dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
else if(town->ID == 33 || town->ID == 219) else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed); dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
else else
return false; return false;

View File

@ -3,10 +3,10 @@
#include "BattleHex.h" #include "BattleHex.h"
#include "../client/FunctionList.h" #include "../client/FunctionList.h"
#include "CObstacleInstance.h"
#include "ResourceSet.h" #include "ResourceSet.h"
#include "int3.h" #include "int3.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "CBattleCallback.h"
/* /*
* IGameCallback.h, part of VCMI engine * IGameCallback.h, part of VCMI engine
@ -59,85 +59,6 @@ struct TeamState;
struct QuestInfo; struct QuestInfo;
class CGCreature; class CGCreature;
typedef std::vector<const CStack*> TStacks;
namespace boost
{class shared_mutex;}
class DLL_LINKAGE CCallbackBase
{
protected:
CGameState *gs;
int player; // -1 gives access to all information, otherwise limited to knowledge of given player
CCallbackBase(CGameState *GS, int Player)
: gs(GS), player(Player)
{}
CCallbackBase()
: gs(NULL), player(-1)
{}
public:
boost::shared_mutex &getGsMutex(); //just return a reference to mutex, does not lock nor anything
};
class DLL_LINKAGE CBattleInfoCallback : public virtual CCallbackBase
{
public:
enum EStackOwnership
{
ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY
};
enum ERandomSpell
{
RANDOM_GENIE, RANDOM_AIMED
};
//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(BattleHex tile); //returns bitfield
shared_ptr<const CObstacleInstance> battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking = true);
std::vector<shared_ptr<const 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(BattleHex pos, bool onlyAlive = true); //returns stack info by given pos
BattleHex battleGetPos(int stack); //returns position (tile ID) of stack
TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true); //returns stacks on battlefield
void getStackQueue( std::vector<const CStack *> &out, int howMany ); //returns vector of stack in order of their move sequence
void battleGetStackCountOutsideHexes(bool *ac); // returns hexes which when in front of a stack cause us to move the amount box back
std::vector<BattleHex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * attackable = NULL); //returns numbers of hexes reachable by creature with id ID
std::vector<int> battleGetDistances(const CStack * stack, BattleHex hex = BattleHex::INVALID, BattleHex * predecessors = NULL); //returns vector of distances to [dest hex number]
std::set<BattleHex> battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID);
bool battleCanShoot(const CStack * stack, BattleHex dest); //returns true if unit with id ID can shoot to dest
bool battleCanCastSpell(); //returns true, if caller can cast a spell
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell); //determines if given spell can be casted (and returns problem description)
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell, BattleHex destination); //if hero can cast spell here
ESpellCastProblem::ESpellCastProblem battleCanCreatureCastThisSpell(const CSpell * spell, BattleHex destination); //determines if creature can cast a spell here
si32 battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode);
bool battleCanFlee(); //returns true if caller can flee from the battle
int battleGetSurrenderCost(); //returns cost of surrendering battle, -1 if surrendering is not possible
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(BattleHex hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found
std::pair<ui32, ui32> battleEstimateDamage(const CStack * attacker, const CStack * defender, std::pair<ui32, ui32> * retaliationDmg = NULL); //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 to given side (0 - attacker, 1 - defender)
si8 battleHasDistancePenalty(const CStack * stack, BattleHex destHex); //checks if given stack has distance penalty
si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex); //checks if given stack has wall penalty
si8 battleHasShootingPenalty(const CStack * stack, BattleHex destHex)
{
return battleHasDistancePenalty(stack, destHex) || battleHasWallPenalty(stack, destHex);
}
si8 battleCanTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel); //checks if teleportation of given stack to given position can take place
si8 battleGetTacticDist(); //returns tactic distance for calling player or 0 if player is not in tactic phase
ui8 battleGetMySide(); //return side of player in battle (attacker/defender)
int battleGetMoatDmg(); //what dmg unit will suffer if ending turn in the moat
//convenience methods using the ones above
TStacks battleGetAllStacks() //returns all stacks, alive or dead or undead or mechanical :)
{
return battleGetStacks(MINE_AND_ENEMY, false);
}
};
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
{ {

View File

@ -1113,6 +1113,7 @@ void BattleStackMoved::applyGs( CGameState *gs )
DLL_LINKAGE void BattleStackAttacked::applyGs( CGameState *gs ) DLL_LINKAGE void BattleStackAttacked::applyGs( CGameState *gs )
{ {
CStack * at = gs->curB->getStack(stackAttacked); CStack * at = gs->curB->getStack(stackAttacked);
assert(at);
at->count = newAmount; at->count = newAmount;
at->firstHPleft = newHP; at->firstHPleft = newHP;
@ -1334,16 +1335,13 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs( CGameState *gs )
CStack * changedStack = gs->curB->getStack(healedStacks[g].stackID, false); CStack * changedStack = gs->curB->getStack(healedStacks[g].stackID, false);
//checking if we resurrect a stack that is under a living stack //checking if we resurrect a stack that is under a living stack
std::vector<BattleHex> access = gs->curB->getAccessibility(changedStack, true); auto accessibility = gs->curB->getAccesibility();
bool acc[GameConstants::BFIELD_SIZE];
for(int h=0; h<GameConstants::BFIELD_SIZE; ++h) if(!changedStack->alive() && !accessibility.accessible(changedStack->position, changedStack))
acc[h] = false; {
for(int h=0; h<access.size(); ++h) tlog1 << "Cannot resurrect " << changedStack->nodeName() << " because hex " << changedStack->position << " is occupied!\n";
acc[access[h]] = true;
if(!changedStack->alive() && !gs->curB->isAccessible(changedStack->position, acc,
changedStack->doubleWide(), changedStack->attackerOwned,
changedStack->hasBonusOfType(Bonus::FLYING), true))
return; //position is already occupied return; //position is already occupied
}
//applying changes //applying changes
bool resurrected = !changedStack->alive(); //indicates if stack is resurrected or just healed bool resurrected = !changedStack->alive(); //indicates if stack is resurrected or just healed

View File

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <climits>
typedef si32 TResource; typedef si32 TResource;
typedef si64 TResourceCap; //to avoid overflow when adding integers. Signed values are easier to control. typedef si64 TResourceCap; //to avoid overflow when adding integers. Signed values are easier to control.

View File

@ -230,14 +230,13 @@
<ClCompile Include="CCreatureHandler.cpp" /> <ClCompile Include="CCreatureHandler.cpp" />
<ClCompile Include="CCreatureSet.cpp" /> <ClCompile Include="CCreatureSet.cpp" />
<ClCompile Include="CDefObjInfoHandler.cpp" /> <ClCompile Include="CDefObjInfoHandler.cpp" />
<ClCompile Include="CFileUtility.cpp" />
<ClCompile Include="CGameInterface.cpp" /> <ClCompile Include="CGameInterface.cpp" />
<ClCompile Include="CGameState.cpp" /> <ClCompile Include="CGameState.cpp" />
<ClCompile Include="CGeneralTextHandler.cpp" /> <ClCompile Include="CGeneralTextHandler.cpp" />
<ClCompile Include="CHeroHandler.cpp" /> <ClCompile Include="CHeroHandler.cpp" />
<ClCompile Include="CLodHandler.cpp" />
<ClCompile Include="CLogger.cpp" /> <ClCompile Include="CLogger.cpp" />
<ClCompile Include="CMapInfo.cpp" /> <ClCompile Include="CMapInfo.cpp" />
<ClCompile Include="CModHandler.cpp" />
<ClCompile Include="CObjectHandler.cpp" /> <ClCompile Include="CObjectHandler.cpp" />
<ClCompile Include="CObstacleInstance.cpp" /> <ClCompile Include="CObstacleInstance.cpp" />
<ClCompile Include="Connection.cpp" /> <ClCompile Include="Connection.cpp" />
@ -245,15 +244,15 @@
<ClCompile Include="CThreadHelper.cpp" /> <ClCompile Include="CThreadHelper.cpp" />
<ClCompile Include="CTownHandler.cpp" /> <ClCompile Include="CTownHandler.cpp" />
<ClCompile Include="Filesystem\CBinaryReader.cpp" /> <ClCompile Include="Filesystem\CBinaryReader.cpp" />
<ClCompile Include="Filesystem\CCompressedStream.cpp" />
<ClCompile Include="Filesystem\CFileInfo.cpp" /> <ClCompile Include="Filesystem\CFileInfo.cpp" />
<ClCompile Include="Filesystem\CFileInputStream.cpp" /> <ClCompile Include="Filesystem\CFileInputStream.cpp" />
<ClCompile Include="Filesystem\CFilesystemLoader.cpp" /> <ClCompile Include="Filesystem\CFilesystemLoader.cpp" />
<ClCompile Include="Filesystem\CLodArchiveLoader.cpp" /> <ClCompile Include="Filesystem\CLodArchiveLoader.cpp" />
<ClCompile Include="Filesystem\CLodStream.cpp" />
<ClCompile Include="Filesystem\CMemoryStream.cpp" /> <ClCompile Include="Filesystem\CMemoryStream.cpp" />
<ClCompile Include="Filesystem\CResourceLoader.cpp" /> <ClCompile Include="Filesystem\CResourceLoader.cpp" />
<ClCompile Include="Filesystem\ISimpleResourceLoader.cpp" />
<ClCompile Include="HeroBonus.cpp" /> <ClCompile Include="HeroBonus.cpp" />
<ClCompile Include="CBattleCallback.cpp" />
<ClCompile Include="IGameCallback.cpp" /> <ClCompile Include="IGameCallback.cpp" />
<ClCompile Include="JsonNode.cpp" /> <ClCompile Include="JsonNode.cpp" />
<ClCompile Include="map.cpp" /> <ClCompile Include="map.cpp" />
@ -283,14 +282,13 @@
<ClInclude Include="CCreatureHandler.h" /> <ClInclude Include="CCreatureHandler.h" />
<ClInclude Include="CCreatureSet.h" /> <ClInclude Include="CCreatureSet.h" />
<ClInclude Include="CDefObjInfoHandler.h" /> <ClInclude Include="CDefObjInfoHandler.h" />
<ClInclude Include="CFileUtility.h" />
<ClInclude Include="CGameInterface.h" /> <ClInclude Include="CGameInterface.h" />
<ClInclude Include="CGameState.h" /> <ClInclude Include="CGameState.h" />
<ClInclude Include="CGeneralTextHandler.h" /> <ClInclude Include="CGeneralTextHandler.h" />
<ClInclude Include="CHeroHandler.h" /> <ClInclude Include="CHeroHandler.h" />
<ClInclude Include="CLodHandler.h" />
<ClInclude Include="CLogger.h" /> <ClInclude Include="CLogger.h" />
<ClInclude Include="CMapInfo.h" /> <ClInclude Include="CMapInfo.h" />
<ClInclude Include="CModHandler.h" />
<ClInclude Include="CObjectHandler.h" /> <ClInclude Include="CObjectHandler.h" />
<ClInclude Include="CObstacleInstance.h" /> <ClInclude Include="CObstacleInstance.h" />
<ClInclude Include="CondSh.h" /> <ClInclude Include="CondSh.h" />
@ -302,17 +300,18 @@
<ClInclude Include="CThreadHelper.h" /> <ClInclude Include="CThreadHelper.h" />
<ClInclude Include="CTownHandler.h" /> <ClInclude Include="CTownHandler.h" />
<ClInclude Include="Filesystem\CBinaryReader.h" /> <ClInclude Include="Filesystem\CBinaryReader.h" />
<ClInclude Include="Filesystem\CCompressedStream.h" />
<ClInclude Include="Filesystem\CFileInfo.h" /> <ClInclude Include="Filesystem\CFileInfo.h" />
<ClInclude Include="Filesystem\CFileInputStream.h" /> <ClInclude Include="Filesystem\CFileInputStream.h" />
<ClInclude Include="Filesystem\CFilesystemLoader.h" /> <ClInclude Include="Filesystem\CFilesystemLoader.h" />
<ClInclude Include="Filesystem\CInputStream.h" /> <ClInclude Include="Filesystem\CInputStream.h" />
<ClInclude Include="Filesystem\CLodArchiveLoader.h" /> <ClInclude Include="Filesystem\CLodArchiveLoader.h" />
<ClInclude Include="Filesystem\CLodStream.h" />
<ClInclude Include="Filesystem\CMemoryStream.h" /> <ClInclude Include="Filesystem\CMemoryStream.h" />
<ClInclude Include="Filesystem\CResourceLoader.h" /> <ClInclude Include="Filesystem\CResourceLoader.h" />
<ClInclude Include="Filesystem\ISimpleResourceLoader.h" /> <ClInclude Include="Filesystem\ISimpleResourceLoader.h" />
<ClInclude Include="GameConstants.h" /> <ClInclude Include="GameConstants.h" />
<ClInclude Include="HeroBonus.h" /> <ClInclude Include="HeroBonus.h" />
<ClInclude Include="CBattleCallback.h" />
<ClInclude Include="IGameCallback.h" /> <ClInclude Include="IGameCallback.h" />
<ClInclude Include="IGameEventsReceiver.h" /> <ClInclude Include="IGameEventsReceiver.h" />
<ClInclude Include="int3.h" /> <ClInclude Include="int3.h" />

View File

@ -531,8 +531,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
ConstTransitivePtr <CGHeroInstance> loserHero = battleResult.data->winner != 0 ? hero1 : hero2; ConstTransitivePtr <CGHeroInstance> loserHero = battleResult.data->winner != 0 ? hero1 : hero2;
std::vector<ui32> arts; //display them in window std::vector<ui32> arts; //display them in window
//TODO: display loot in window if (result == BattleResult::NORMAL && winnerHero)
if (result < BattleResult::SURRENDER && winnerHero)
{ {
if (loserHero) if (loserHero)
{ {
@ -542,7 +541,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
MoveArtifact ma; MoveArtifact ma;
ma.src = ArtifactLocation (loserHero, artSlot.first); ma.src = ArtifactLocation (loserHero, artSlot.first);
const CArtifactInstance * art = ma.src.getArt(); const CArtifactInstance * art = ma.src.getArt();
if (art && !art->artType->isBig()) // don't move war machines or locked arts (spellbook) if (art && !art->artType->isBig() && art->id != ArtifactId::SPELLBOOK) // don't move war machines or locked arts (spellbook)
{ {
arts.push_back (art->artType->id); arts.push_back (art->artType->id);
ma.dst = ArtifactLocation (winnerHero, art->firstAvailableSlot(winnerHero)); ma.dst = ArtifactLocation (winnerHero, art->firstAvailableSlot(winnerHero));
@ -692,6 +691,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
addToSlot(StackLocation(winnerHero, necroSlot), raisedStack.type, raisedStack.count); addToSlot(StackLocation(winnerHero, necroSlot), raisedStack.type, raisedStack.count);
} }
sendAndApply(&resultsApplied); sendAndApply(&resultsApplied);
setBattle(nullptr);
if(duel) if(duel)
{ {
@ -775,10 +775,10 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
if (!bat.shot()) //multiple-hex attack - only in meele if (!bat.shot()) //multiple-hex attack - only in meele
{ {
std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex); std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex);
//TODO: get exact attacked hex for defender //TODO: get exact attacked hex for defender
BOOST_FOREACH(CStack * stack, attackedCreatures) BOOST_FOREACH(const CStack * stack, attackedCreatures)
{ {
if (stack != def) //do not hit same stack twice if (stack != def) //do not hit same stack twice
{ {
@ -793,10 +793,10 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.bsa.front().flags |= BattleStackAttacked::EFFECT; bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
bat.bsa.front().effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect? bat.bsa.front().effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect?
std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, targetHex); std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, targetHex);
//TODO: get exact attacked hex for defender //TODO: get exact attacked hex for defender
BOOST_FOREACH(CStack * stack, attackedCreatures) BOOST_FOREACH(const CStack * stack, attackedCreatures)
{ {
if (stack != def) //do not hit same stack twice if (stack != def) //do not hit same stack twice
{ {
@ -924,8 +924,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
{ {
int ret = 0; int ret = 0;
CStack *curStack = gs->curB->getStack(stack), const CStack *curStack = gs->curB->battleGetStackByID(stack),
*stackAtEnd = gs->curB->getStackT(dest); *stackAtEnd = gs->curB->battleGetStackByPos(dest);
assert(curStack); assert(curStack);
assert(dest < GameConstants::BFIELD_SIZE); assert(dest < GameConstants::BFIELD_SIZE);
@ -936,50 +936,30 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
} }
//initing necessary tables //initing necessary tables
bool accessibility[GameConstants::BFIELD_SIZE]; auto accessibility = getAccesibility();
std::vector<BattleHex> accessible = gs->curB->getAccessibility(curStack, false, NULL, true);
for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
{
accessibility[b] = false;
}
for(int g=0; g<accessible.size(); ++g)
{
accessibility[accessible[g]] = true;
}
//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there) //shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
if(!stackAtEnd && curStack->doubleWide() && !accessibility[dest]) if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack))
{ {
if(curStack->attackerOwned) if(curStack->attackerOwned)
{ {
if(accessibility[dest+1]) if(accessibility.accessible(dest+1, curStack))
dest += BattleHex::RIGHT; dest += BattleHex::RIGHT;
} }
else else
{ {
if(accessibility[dest-1]) if(accessibility.accessible(dest-1, curStack))
dest += BattleHex::LEFT; dest += BattleHex::LEFT;
} }
} }
if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility[dest]) if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility.accessible(dest, curStack))
{
complain("Given destination is not accessible!");
return 0; return 0;
bool accessibilityWithOccupyable[GameConstants::BFIELD_SIZE];
std::vector<BattleHex> accOc = gs->curB->getAccessibility(curStack, true, NULL, true);
for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
{
accessibilityWithOccupyable[b] = false;
}
for(int g=0; g<accOc.size(); ++g)
{
accessibilityWithOccupyable[accOc[g]] = true;
} }
//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex std::pair< std::vector<BattleHex>, int > path = gs->curB->getPath(curStack->position, dest, curStack);
// return false;
std::pair< std::vector<BattleHex>, int > path = gs->curB->getPath(curStack->position, dest, accessibilityWithOccupyable, curStack->hasBonusOfType(Bonus::FLYING), curStack->doubleWide(), curStack->attackerOwned);
ret = path.second; ret = path.second;
@ -3220,6 +3200,40 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
tlog1 << "\tMaking action of type " << ba.actionType << std::endl; tlog1 << "\tMaking action of type " << ba.actionType << std::endl;
bool ok = true; bool ok = true;
const CStack *stack = battleGetStackByID(ba.stackNumber); //may be nullptr if action is not about stack
const bool isAboutActiveStack = stack && (stack == battleActiveStack());
switch(ba.actionType)
{
case BattleAction::WALK: //walk
case BattleAction::DEFEND: //defend
case BattleAction::WAIT: //wait
case BattleAction::WALK_AND_ATTACK: //walk or attack
case BattleAction::SHOOT: //shoot
case BattleAction::CATAPULT: //catapult
case BattleAction::STACK_HEAL: //healing with First Aid Tent
case BattleAction::DAEMON_SUMMONING:
case BattleAction::MONSTER_SPELL:
if(!stack)
{
complain("No such stack!");
return false;
}
if(!stack->alive())
{
complain("This stack is dead: " + stack->nodeName());
return false;
}
if(!isAboutActiveStack)
{
complain("Action has to be about active stack!");
return false;
}
}
switch(ba.actionType) switch(ba.actionType)
{ {
case BattleAction::END_TACTIC_PHASE: //wait case BattleAction::END_TACTIC_PHASE: //wait
@ -3267,7 +3281,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
case BattleAction::SURRENDER: case BattleAction::SURRENDER:
{ {
int player = gs->curB->sides[ba.side]; int player = gs->curB->sides[ba.side];
int cost = gs->curB->getSurrenderingCost(player); int cost = gs->curB->battleGetSurrenderCost(player);
if(cost < 0) if(cost < 0)
complain("Cannot surrender!"); complain("Cannot surrender!");
else if(getResource(player, Res::GOLD) < cost) else if(getResource(player, Res::GOLD) < cost)
@ -3284,10 +3298,10 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{ {
StartAction start_action(ba); StartAction start_action(ba);
sendAndApply(&start_action); //start movement and attack sendAndApply(&start_action); //start movement and attack
int startingPos = gs->curB->getStack(ba.stackNumber)->position; int startingPos = gs->curB->battleGetStackByID(ba.stackNumber)->position;
int distance = moveStack(ba.stackNumber, ba.destinationTile); int distance = moveStack(ba.stackNumber, ba.destinationTile);
CStack *curStack = gs->curB->getStack(ba.stackNumber), const CStack *curStack = gs->curB->battleGetStackByID(ba.stackNumber),
*stackAtEnd = gs->curB->getStackT(ba.additionalInfo); *stackAtEnd = gs->curB->battleGetStackByPos(ba.additionalInfo);
if(!curStack || !stackAtEnd) if(!curStack || !stackAtEnd)
{ {
@ -3295,6 +3309,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
break; break;
} }
tlog5 << curStack->nodeName() << " will attack " << stackAtEnd->nodeName() << std::endl;
if(curStack->position != ba.destinationTile //we wasn't able to reach destination tile if(curStack->position != ba.destinationTile //we wasn't able to reach destination tile
&& !(curStack->doubleWide() && !(curStack->doubleWide()
&& ( curStack->position == ba.destinationTile + (curStack->attackerOwned ? +1 : -1 ) ) && ( curStack->position == ba.destinationTile + (curStack->attackerOwned ? +1 : -1 ) )
@ -3341,7 +3357,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
//counterattack //counterattack
if(!curStack->hasBonusOfType(Bonus::BLOCKS_RETALIATION) if(!curStack->hasBonusOfType(Bonus::BLOCKS_RETALIATION)
&& stackAtEnd->ableToRetaliate()) && stackAtEnd->ableToRetaliate()
&& curStack->alive()) //attacker may have died (fire shield)
{ {
BattleAttack bat; BattleAttack bat;
prepareAttack(bat, stackAtEnd, curStack, 0, curStack->position); prepareAttack(bat, stackAtEnd, curStack, 0, curStack->position);
@ -3374,8 +3391,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
case BattleAction::SHOOT: //shoot case BattleAction::SHOOT: //shoot
{ {
CStack *curStack = gs->curB->getStack(ba.stackNumber), const CStack *curStack = gs->curB->battleGetStackByID(ba.stackNumber),
*destStack= gs->curB->getStackT(ba.destinationTile); *destStack= gs->curB->battleGetStackByPos(ba.destinationTile);
if( !gs->curB->battleCanShoot(curStack, ba.destinationTile) ) if( !gs->curB->battleCanShoot(curStack, ba.destinationTile) )
break; break;
@ -3423,7 +3440,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(CGHeroInstance::BALLISTICS)]; CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(CGHeroInstance::BALLISTICS)];
int attackedPart = gs->curB->hexToWallPart(ba.destinationTile); int attackedPart = gs->curB->battleHexToWallPart(ba.destinationTile);
if(attackedPart < 0) if(attackedPart < 0)
{ {
complain("catapult tried to attack non-catapultable hex!"); complain("catapult tried to attack non-catapultable hex!");
@ -3524,8 +3541,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
StartAction start_action(ba); StartAction start_action(ba);
sendAndApply(&start_action); sendAndApply(&start_action);
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side]; const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
CStack *healer = gs->curB->getStack(ba.stackNumber), const CStack *healer = gs->curB->battleGetStackByID(ba.stackNumber),
*destStack = gs->curB->getStackT(ba.destinationTile); *destStack = gs->curB->battleGetStackByPos(ba.destinationTile);
if(healer == NULL || destStack == NULL || !healer->hasBonusOfType(Bonus::HEALER)) if(healer == NULL || destStack == NULL || !healer->hasBonusOfType(Bonus::HEALER))
{ {
@ -3567,8 +3584,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
StartAction start_action(ba); StartAction start_action(ba);
sendAndApply(&start_action); sendAndApply(&start_action);
CStack *summoner = gs->curB->getStack(ba.stackNumber), const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber),
*destStack = gs->curB->getStackT(ba.destinationTile, false); *destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false);
BattleStackAdded bsa; BattleStackAdded bsa;
bsa.attacker = summoner->attackerOwned; bsa.attacker = summoner->attackerOwned;
@ -3603,7 +3620,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
StartAction start_action(ba); StartAction start_action(ba);
sendAndApply(&start_action); sendAndApply(&start_action);
CStack * stack = gs->curB->getStack(ba.stackNumber); const CStack * stack = gs->curB->battleGetStackByID(ba.stackNumber);
int spellID = ba.additionalInfo; int spellID = ba.additionalInfo;
BattleHex destination(ba.destinationTile); BattleHex destination(ba.destinationTile);
@ -3852,7 +3869,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
if (caster) //calculate spell cost if (caster) //calculate spell cost
{ {
sc.spellCost = gs->curB->getSpellCost(VLC->spellh->spells[spellID], caster); sc.spellCost = gs->curB->battleGetSpellCost(VLC->spellh->spells[spellID], caster);
if (secHero && mode == ECastingMode::HERO_CASTING) //handle mana channel if (secHero && mode == ECastingMode::HERO_CASTING) //handle mana channel
{ {
@ -3869,18 +3886,18 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
} }
//calculating affected creatures for all spells //calculating affected creatures for all spells
std::set<CStack*> attackedCres; std::set<const CStack*> attackedCres;
if (mode != ECastingMode::ENCHANTER_CASTING) if (mode != ECastingMode::ENCHANTER_CASTING)
{ {
attackedCres = gs->curB->getAttackedCreatures(spell, spellLvl, casterColor, destination); attackedCres = gs->curB->getAttackedCreatures(spell, spellLvl, casterColor, destination);
for(std::set<CStack*>::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(std::set<const CStack*>::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
sc.affectedCres.insert((*it)->ID); sc.affectedCres.insert((*it)->ID);
} }
} }
else //enchanter - hit all possible stacks else //enchanter - hit all possible stacks
{ {
BOOST_FOREACH (CStack * stack, gs->curB->stacks) BOOST_FOREACH (const CStack * stack, gs->curB->stacks)
{ {
/*if it's non negative spell and our unit or non positive spell and hostile unit */ /*if it's non negative spell and our unit or non positive spell and hostile unit */
if((!spell->isNegative() && stack->owner == casterColor) if((!spell->isNegative() && stack->owner == casterColor)
@ -3898,7 +3915,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
sc.resisted = gs->curB->calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor, mode, usedSpellPower, spellLvl); sc.resisted = gs->curB->calculateResistedStacks(spell, caster, secHero, attackedCres, casterColor, mode, usedSpellPower, spellLvl);
//calculating dmg to display //calculating dmg to display
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(std::set<const CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue; continue;
@ -3976,7 +3993,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
} }
} }
int chainLightningModifier = 0; int chainLightningModifier = 0;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue; continue;
@ -4067,7 +4084,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
bonus = caster->getBonus(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, spellID)); bonus = caster->getBonus(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, spellID));
si32 power = 0; si32 power = 0;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue; continue;
@ -4154,7 +4171,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
StacksHealedOrResurrected shr; StacksHealedOrResurrected shr;
shr.lifeDrain = (ui8)false; shr.lifeDrain = (ui8)false;
shr.tentHealing = (ui8)false; shr.tentHealing = (ui8)false;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell
|| (spellID == Spells::ANIMATE_DEAD && !(*it)->hasBonusOfType(Bonus::UNDEAD)) //we try to cast animate dead on living stack || (spellID == Spells::ANIMATE_DEAD && !(*it)->hasBonusOfType(Bonus::UNDEAD)) //we try to cast animate dead on living stack
@ -4172,7 +4189,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
hi.healedHP = gs->curB->calculateHealedHP(spell, usedSpellPower, spellLvl, *it); hi.healedHP = gs->curB->calculateHealedHP(spell, usedSpellPower, spellLvl, *it);
} }
else else
hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it, gs->curB->getStack(selectedStack)); hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it, gs->curB->battleGetStackByID(selectedStack));
hi.lowLevelResurrection = spellLvl <= 1; hi.lowLevelResurrection = spellLvl <= 1;
shr.healedStacks.push_back(hi); shr.healedStacks.push_back(hi);
} }
@ -4227,7 +4244,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
break; break;
case Spells::CLONE: case Spells::CLONE:
{ {
CStack * clonedStack = NULL; const CStack * clonedStack = NULL;
if (attackedCres.size()) if (attackedCres.size())
clonedStack = *attackedCres.begin(); clonedStack = *attackedCres.begin();
if (!clonedStack) if (!clonedStack)
@ -4270,7 +4287,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
break; break;
case Spells::DEATH_STARE: //handled in a bit different way case Spells::DEATH_STARE: //handled in a bit different way
{ {
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
if((*it)->hasBonusOfType(Bonus::UNDEAD) || (*it)->hasBonusOfType(Bonus::NON_LIVING)) //this creature is immune if((*it)->hasBonusOfType(Bonus::UNDEAD) || (*it)->hasBonusOfType(Bonus::NON_LIVING)) //this creature is immune
{ {
@ -4291,7 +4308,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
break; break;
case Spells::ACID_BREATH_DAMAGE: //new effect, separate from acid breath defense reduction case Spells::ACID_BREATH_DAMAGE: //new effect, separate from acid breath defense reduction
{ {
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) //no immunities for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it) //no immunities
{ {
BattleStackAttacked bsa; BattleStackAttacked bsa;
bsa.flags |= BattleStackAttacked::EFFECT; bsa.flags |= BattleStackAttacked::EFFECT;
@ -4323,7 +4340,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
//Magic Mirror effect //Magic Mirror effect
if (spell->isNegative() && mode != ECastingMode::MAGIC_MIRROR && spell->level && spell->range[0] == "0") //it is actual spell and can be reflected to single target, no recurrence if (spell->isNegative() && mode != ECastingMode::MAGIC_MIRROR && spell->level && spell->range[0] == "0") //it is actual spell and can be reflected to single target, no recurrence
{ {
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) for(auto it = attackedCres.begin(); it != attackedCres.end(); ++it)
{ {
int mirrorChance = (*it)->valOfBonuses(Bonus::MAGIC_MIRROR); int mirrorChance = (*it)->valOfBonuses(Bonus::MAGIC_MIRROR);
if(mirrorChance > rand()%100) if(mirrorChance > rand()%100)
@ -4388,7 +4405,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
ECastingMode::HERO_CASTING, NULL, ba.selectedStack); ECastingMode::HERO_CASTING, NULL, ba.selectedStack);
sendAndApply(&end_action); sendAndApply(&end_action);
if( !gs->curB->getStack(gs->curB->activeStack, false)->alive() ) if( !gs->curB->battleGetStackByID(gs->curB->activeStack, false)->alive() )
{ {
battleMadeAction.setn(true); battleMadeAction.setn(true);
} }
@ -4426,11 +4443,11 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
{ {
bool unbind = true; bool unbind = true;
BonusList bl = *(st->getBonuses(Selector::type(Bonus::BIND_EFFECT))); BonusList bl = *(st->getBonuses(Selector::type(Bonus::BIND_EFFECT)));
std::set<CStack*> stacks = gs->curB->getAdjacentCreatures(st); std::set<const CStack*> stacks = gs->curB-> batteAdjacentCreatures(st);
BOOST_FOREACH(Bonus * b, bl) BOOST_FOREACH(Bonus * b, bl)
{ {
const CStack * stack = gs->curB->getStack(b->additionalInfo); //binding stack must be alive and adjacent const CStack * stack = gs->curB->battleGetStackByID(b->additionalInfo); //binding stack must be alive and adjacent
if (stack) if (stack)
{ {
if (vstd::contains(stacks, stack)) //binding stack is still present if (vstd::contains(stacks, stack)) //binding stack is still present
@ -4533,7 +4550,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
} }
} }
void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, CStack * curStack) void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack)
{ {
//we want to determine following vars depending on obstacle type //we want to determine following vars depending on obstacle type
int damage = -1; int damage = -1;
@ -4552,7 +4569,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, C
else if(obstacle.obstacleType == CObstacleInstance::LAND_MINE) else if(obstacle.obstacleType == CObstacleInstance::LAND_MINE)
{ {
//You don't get hit by a Mine you can see. //You don't get hit by a Mine you can see.
if(gs->curB->isObstacleVisibleForSide(obstacle, side)) if(gs->curB->battleIsObstacleVisibleForSide(obstacle, (BattlePerspective::BattlePerspective)side))
return; return;
oneTimeObstacle = true; oneTimeObstacle = true;
@ -5168,7 +5185,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
{ {
if (bat.bsa[g].newAmount > 0 && !bat.bsa[g].isSecondary()) //apply effects only to first target stack if it's alive if (bat.bsa[g].newAmount > 0 && !bat.bsa[g].isSecondary()) //apply effects only to first target stack if it's alive
{ {
oneOfAttacked = gs->curB->getStack(bat.bsa[g].stackAttacked); oneOfAttacked = gs->curB->battleGetStackByID(bat.bsa[g].stackAttacked);
break; break;
} }
} }
@ -5206,13 +5223,13 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat) void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat)
{ {
const CStack * attacker = gs->curB->getStack(bat.stackAttacking); const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking);
attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no detah stare / acid bretah needed? attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no detah stare / acid bretah needed?
} }
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
{ {
const CStack * attacker = gs->curB->getStack(bat.stackAttacking); const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking);
if (!attacker) //could be already dead if (!attacker) //could be already dead
return; return;
attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker); attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker);
@ -5241,7 +5258,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
if (staredCreatures) if (staredCreatures)
{ {
if (bat.bsa.size() && bat.bsa[0].newAmount > 0) //TODO: death stare was not originally available for multiple-hex attacks, but... if (bat.bsa.size() && bat.bsa[0].newAmount > 0) //TODO: death stare was not originally available for multiple-hex attacks, but...
handleSpellCasting(79, 0, gs->curB->getStack(bat.bsa[0].stackAttacked)->position, handleSpellCasting(79, 0, gs->curB->battleGetStackByID(bat.bsa[0].stackAttacked)->position,
!attacker->attackerOwned, attacker->owner, NULL, NULL, staredCreatures, ECastingMode::AFTER_ATTACK_CASTING, attacker); !attacker->attackerOwned, attacker->owner, NULL, NULL, staredCreatures, ECastingMode::AFTER_ATTACK_CASTING, attacker);
} }
} }
@ -5255,7 +5272,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
} }
if (acidDamage) if (acidDamage)
{ {
handleSpellCasting(81, 0, gs->curB->getStack(bat.bsa[0].stackAttacked)->position, handleSpellCasting(81, 0, gs->curB->battleGetStackByID(bat.bsa[0].stackAttacked)->position,
!attacker->attackerOwned, attacker->owner, NULL, NULL, !attacker->attackerOwned, attacker->owner, NULL, NULL,
acidDamage * attacker->count, ECastingMode::AFTER_ATTACK_CASTING, attacker); acidDamage * attacker->count, ECastingMode::AFTER_ATTACK_CASTING, attacker);
} }
@ -5716,6 +5733,7 @@ bool CGameHandler::swapStacks(const StackLocation &sl1, const StackLocation &sl2
void CGameHandler::runBattle() void CGameHandler::runBattle()
{ {
setBattle(gs->curB);
assert(gs->curB); assert(gs->curB);
//TODO: pre-tactic stuff, call scripts etc. //TODO: pre-tactic stuff, call scripts etc.

View File

@ -201,7 +201,7 @@ public:
int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack = -1); int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack = -1);
bool makeCustomAction(BattleAction &ba); bool makeCustomAction(BattleAction &ba);
void stackTurnTrigger(const CStack * stack); void stackTurnTrigger(const CStack * stack);
void handleDamageFromObstacle(const CObstacleInstance &obstacle, CStack * curStack); //checks if obstacle is land mine and handles possible consequences void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
void removeObstacle(const CObstacleInstance &obstacle); void removeObstacle(const CObstacleInstance &obstacle);
bool queryReply( ui32 qid, ui32 answer, ui8 player ); bool queryReply( ui32 qid, ui32 answer, ui8 player );
bool hireHero( const CGObjectInstance *obj, ui8 hid, ui8 player ); bool hireHero( const CGObjectInstance *obj, ui8 hid, ui8 player );

View File

@ -251,7 +251,7 @@ bool MakeAction::applyGh( CGameHandler *gh )
if(gh->connections[b->sides[b->tacticsSide]] != c) if(gh->connections[b->sides[b->tacticsSide]] != c)
ERROR_AND_RETURN; ERROR_AND_RETURN;
} }
else if(gh->connections[b->getStack(b->activeStack)->owner] != c) else if(gh->connections[b->battleGetStackByID(b->activeStack)->owner] != c)
ERROR_AND_RETURN; ERROR_AND_RETURN;
return gh->makeBattleAction(ba); return gh->makeBattleAction(ba);
@ -262,9 +262,10 @@ bool MakeCustomAction::applyGh( CGameHandler *gh )
const BattleInfo *b = GS(gh)->curB; const BattleInfo *b = GS(gh)->curB;
if(!b) ERROR_AND_RETURN; if(!b) ERROR_AND_RETURN;
if(b->tacticDistance) ERROR_AND_RETURN; if(b->tacticDistance) ERROR_AND_RETURN;
const CStack *active = GS(gh)->curB->getStack(GS(gh)->curB->activeStack); const CStack *active = GS(gh)->curB->battleGetStackByID(GS(gh)->curB->activeStack);
if(!active) ERROR_AND_RETURN; if(!active) ERROR_AND_RETURN;
if(gh->connections[active->owner] != c) ERROR_AND_RETURN; if(gh->connections[active->owner] != c) ERROR_AND_RETURN;
if(ba.actionType != BattleAction::HERO_SPELL) ERROR_AND_RETURN;
return gh->makeCustomAction(ba); return gh->makeCustomAction(ba);
} }