File: BattleState.h

package info (click to toggle)
vcmi 0.99%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: stretch
  • size: 10,264 kB
  • ctags: 16,826
  • sloc: cpp: 121,945; objc: 248; sh: 193; makefile: 28; python: 13; ansic: 9
file content (305 lines) | stat: -rw-r--r-- 14,859 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
 * BattleState.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
 * License: GNU General Public License v2.0 or later
 * Full text of license available in license.txt file, in main folder
 *
 */

#pragma once

#include "BattleHex.h"
#include "mapObjects/CArmedInstance.h" // for army serialization
#include "mapObjects/CGHeroInstance.h" // for commander serialization
#include "CCreatureHandler.h"
#include "ConstTransitivePtr.h"
#include "GameConstants.h"
#include "CBattleCallback.h"
#include "int3.h"
#include "spells/Magic.h"

class CGHeroInstance;
class CStack;
class CArmedInstance;
class CGTownInstance;
class CStackInstance;
struct BattleStackAttacked;
class CRandomGenerator;


//only for use in BattleInfo
struct DLL_LINKAGE SiegeInfo
{
	std::array<si8, EWallPart::PARTS_COUNT> wallState;
	EGateState gateState;

	SiegeInfo();

	// return EWallState decreased by value of damage points
	static EWallState::EWallState applyDamage(EWallState::EWallState state, unsigned int value)
	{
		if (value == 0)
			return state;

		switch (applyDamage(state, value - 1))
		{
		case EWallState::INTACT    : return EWallState::DAMAGED;
		case EWallState::DAMAGED   : return EWallState::DESTROYED;
		case EWallState::DESTROYED : return EWallState::DESTROYED;
		default: return EWallState::NONE;
		}
	}

	template <typename Handler> void serialize(Handler &h, const int version)
	{
		h & wallState & gateState;
	}
};

struct DLL_LINKAGE SideInBattle
{
	PlayerColor color;
	const CGHeroInstance *hero; //may be NULL if army is not commanded by hero
	const CArmedInstance *armyObject; //adv. map object with army that participates in battle; may be same as hero

	ui8 castSpellsCount; //how many spells each side has cast this turn
	std::vector<const CSpell *> usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill
	si16 enchanterCounter; //tends to pass through 0, so sign is needed

	SideInBattle();
	void init(const CGHeroInstance *Hero, const CArmedInstance *Army);


	template <typename Handler> void serialize(Handler &h, const int version)
	{
		h & color & hero & armyObject;
		h & castSpellsCount & usedSpellsHistory & enchanterCounter;
	}
};

struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback
{
	std::array<SideInBattle, 2> sides; //sides[0] - attacker, sides[1] - defender
	si32 round, activeStack, selectedStack;
	const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege)
	int3 tile; //for background and bonuses
	std::vector<CStack*> stacks;
	std::vector<std::shared_ptr<CObstacleInstance> > obstacles;
	SiegeInfo si;

	BFieldType battlefieldType; //like !!BA:B
	ETerrainType terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy)

	ui8 tacticsSide; //which side is requested to play tactics phase
	ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line)

	template <typename Handler> void serialize(Handler &h, const int version)
	{
		h & sides;
		h & round & activeStack & selectedStack & town & tile & stacks & obstacles
			& si & battlefieldType & terrainType;
		h & tacticsSide & tacticDistance;
		h & static_cast<CBonusSystemNode&>(*this);
	}

	//////////////////////////////////////////////////////////////////////////
	BattleInfo();
	~BattleInfo(){};

	//////////////////////////////////////////////////////////////////////////
	CStack * getStack(int stackID, bool onlyAlive = true);
	using CBattleInfoEssentials::battleGetArmyObject;
	CArmedInstance * battleGetArmyObject(ui8 side) const;
	using CBattleInfoEssentials::battleGetFightingHero;
	CGHeroInstance * battleGetFightingHero(ui8 side) const;

	const CStack * getNextStack() const; //which stack will have turn after current one
	//void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action

	//void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = nullptr) 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
	int getAvaliableHex(CreatureID 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
	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 = nullptr, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range)

	//bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const;
	std::shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
	std::set<BattleHex> getStoppers(bool whichSidePerspective) const;

	ui32 calculateDmg(const CStack * attacker, const CStack * defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting)
	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)

	//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee
	//std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks
	//std::set<BattleHex> getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks

	CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
	CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID 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

	const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player

	void localInit();

	void localInitStack(CStack * s);
	static BattleInfo * setupBattle( int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
	//bool hasNativeStack(ui8 side) const;

	PlayerColor theOtherPlayer(PlayerColor player) const;
	ui8 whatSide(PlayerColor player) const;

	static BattlefieldBI::BattlefieldBI battlefieldTypeToBI(BFieldType bfieldType); //converts above to ERM BI format
	static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
};

class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor, public ISpellCaster
{
public:
	const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)

	ui32 ID; //unique ID of stack
	ui32 baseAmount;
	ui32 firstHPleft; //HP of first creature in stack
	PlayerColor owner; //owner - player colour (255 for neutrals)
	SlotID slot;  //slot - position in garrison (may be 255 for neutrals/called creatures)
	bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
	BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
	///how many times this stack has been counterattacked this round
	ui8 counterAttacksPerformed;
	///cached total count of counterattacks; should be cleared each round;do not serialize
	mutable ui8 counterAttacksTotalCache;
	si16 shots; //how many shots left
	ui8 casts; //how many casts left
	TQuantity resurrected; // these units will be taken back after battle is over
	///id of alive clone of this stack clone if any
	si32 cloneID;
	std::set<EBattleStackState::EBattleStackState> state;
	//overrides
	const CCreature* getCreature() const {return type;}

	CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor
	CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor
	CStack(); //c-tor
	~CStack();
	std::string nodeName() const override;

	void init(); //set initial (invalid) values
	void postInit(); //used to finish initialization when inheriting creature parameters is working
	std::string getName() const; //plural or singular
	bool willMove(int turn = 0) const; //if stack has remaining move this turn
	bool ableToRetaliate() const; //if stack can retaliate after attacked
	///how many times this stack can counterattack in one round
	ui8 counterAttacksTotal() const;
	///how many times this stack can counterattack in one round more
	si8 counterAttacksRemaining() const;
	bool moved(int turn = 0) const; //if stack was already moved this turn
	bool waited(int turn = 0) const;
	bool canMove(int turn = 0) const; //if stack can move
	bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines
	///returns actual heal value based on internal state
	ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const;
	ui32 level() const;
	si32 magicResistance() const override; //include aura of resistance
	static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
	std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
	const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
	ui32 totalHealth() const; // total health for all creatures in stack;

	static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);

	bool doubleWide() const;
	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(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
	std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size

	std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
	void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled

	///ISpellCaster
	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
	ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;

	///default spell school level for effect calculation
	int getEffectLevel(const CSpell * spell) const override;

	///default spell-power for damage/heal calculation
	int getEffectPower(const CSpell * spell) const override;

	///default spell-power for timed effects duration
	int getEnchantPower(const CSpell * spell) const override;

	///damage/heal override(ignores spell configuration, effect level and effect power)
	int getEffectValue(const CSpell * spell) const override;

	const PlayerColor getOwner() const override;

	void getCasterName(MetaString & text) const override;

	void getCastDescription(const CSpell * spell, const std::vector<const CStack *> & attacked, MetaString & text) const override;

	///stack will be ghost in next battle state update
	void makeGhost();

	template <typename Handler> void serialize(Handler &h, const int version)
	{
		assert(isIndependentNode());
		h & static_cast<CBonusSystemNode&>(*this);
		h & static_cast<CStackBasicDescriptor&>(*this);
		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed
			& shots & casts & count & resurrected;

		const CArmedInstance *army = (base ? base->armyObj : nullptr);
		SlotID slot = (base ? base->armyObj->findStack(base) : SlotID());

		if(h.saving)
		{
			h & army & slot;
		}
		else
		{
			h & army & slot;
			if (slot == SlotID::COMMANDER_SLOT_PLACEHOLDER) //TODO
			{
				auto hero = dynamic_cast<const CGHeroInstance *>(army);
				assert (hero);
				base = hero->commander;
			}
			else if(!army || slot == SlotID() || !army->hasStackAtSlot(slot))
			{
				base = nullptr;
				logGlobal->warnStream() << type->nameSing << " doesn't have a base stack!";
			}
			else
			{
				base = &army->getStack(slot);
			}
		}

	}
	bool alive() const //determines if stack is alive
	{
		return vstd::contains(state,EBattleStackState::ALIVE);
	}

	bool isDead() const;
	bool isGhost() const; //determines if stack was removed
	bool isValidTarget(bool allowDead = false) const; //non-turret non-ghost stacks (can be attacked or be object of magic effect)
	bool isTurret() const;
};

class DLL_LINKAGE CMP_stack
{
	int phase; //rules of which phase will be used
	int turn;
public:

	bool operator ()(const CStack* a, const CStack* b);
	CMP_stack(int Phase = 1, int Turn = 0);
};