Parcourir la source

Implemented Dragon Breath, Attack All Around & Three-Headed Attack.

Dragon Breath does not yet work correctly for all double-wide stacks, though.
DjWarmonger il y a 14 ans
Parent
commit
e95ae22061
6 fichiers modifiés avec 174 ajouts et 85 suppressions
  1. 0 1
      global.h
  2. 110 1
      lib/BattleState.cpp
  3. 2 0
      lib/BattleState.h
  4. 1 1
      lib/NetPacks.h
  5. 60 82
      server/CGameHandler.cpp
  6. 1 0
      server/CGameHandler.h

+ 0 - 1
global.h

@@ -300,7 +300,6 @@ struct THex
 	{
 		h & hex;
 	}
-private:
 	static void checkAndPush(int tile, std::vector<THex> & ret)
 	{
 		if( tile>=0 && tile<BFIELD_SIZE && (tile%BFIELD_WIDTH != (BFIELD_WIDTH - 1)) && (tile%BFIELD_WIDTH != 0) )

+ 110 - 1
lib/BattleState.cpp

@@ -713,7 +713,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 			{
 				if (s->id == 76) //Death Cloud //TODO: fireball and fire immunity
 				{
-					if (st->isLiving() || st->position == destinationTile) //directly hit or alive
+					if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
 					{
 						attackedCres.insert(st);
 					}
@@ -763,6 +763,79 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 	}
 	return attackedCres;
 }
+std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex destinationTile)
+{ //TODO: caching?
+	std::set<CStack*> attackedCres;
+	const int WN = BFIELD_WIDTH;
+	if (attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
+	{
+		std::vector<THex> hexes = attacker->getSurroundingHexes();
+		BOOST_FOREACH (THex tile, hexes)
+		{
+			CStack * st = getStackT(tile);
+			if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
+			{
+				attackedCres.insert(st);
+			}
+		}
+	}
+	ui16 hex = attacker->position.hex;
+	if (attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
+	{
+		std::vector<THex> hexes;
+		if (attacker->attackerOwned)
+		{
+			THex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
+			THex::checkAndPush(hex + 1, hexes);
+			THex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes);
+		}
+		else
+		{
+			THex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes);
+			THex::checkAndPush(hex - 1, hexes);
+			THex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN ), hexes);
+		}
+		BOOST_FOREACH (THex tile, hexes)
+		{
+			CStack * st = getStackT(tile);
+			if(st && st->owner != attacker->owner)
+			{
+				attackedCres.insert(st);
+			}
+		}
+	}
+	if (attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH))
+	{
+		std::vector<THex> hexes; //only one, in fact
+		int pseudoVector = destinationTile.hex - hex;
+		switch (pseudoVector)
+		{
+			case 1:
+			case -1:
+				THex::checkAndPush(destinationTile.hex + pseudoVector, hexes);
+				break;
+			case WN: //17
+			case WN + 1: //18
+			case -WN: //-17
+			case -WN + 1: //-16
+				THex::checkAndPush(destinationTile.hex + pseudoVector + ((hex/WN)%2 ? 1 : -1 ), hexes);
+				break;
+			case WN-1: //16
+			case -WN-1: //-18
+				THex::checkAndPush(destinationTile.hex + pseudoVector + ((hex/WN)%2 ? 0 : -1), hexes);
+				break;
+		}
+		BOOST_FOREACH (THex tile, hexes)
+		{
+			CStack * st = getStackT(tile);
+			if(st) //friendly stacks can also be damaged by Dragon Breath
+			{
+				attackedCres.insert(st);
+			}
+		}
+	}
+	return attackedCres;
+}
 
 int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower )
 {
@@ -2292,6 +2365,42 @@ bool CStack::coversPos(THex pos) const
 	return vstd::contains(getHexes(), pos);
 }
 
+std::vector<THex> CStack::getSurroundingHexes() const
+{
+	std::vector<THex> hexes;
+	if (doubleWide())
+	{
+		const int WN = BFIELD_WIDTH;
+		if(attackerOwned)
+		{ //position is equal to front hex
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN-2 ), hexes);
+			THex::checkAndPush(position.hex - 2, hexes);
+			THex::checkAndPush(position.hex + 1, hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-2 : WN-1 ), hexes);
+			THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
+			THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
+		}
+		else
+		{
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+2 : WN+1 ), hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
+			THex::checkAndPush(position.hex + 2, hexes);
+			THex::checkAndPush(position.hex - 1, hexes);
+			THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
+			THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
+			THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN+1 : WN+2 ), hexes);
+		}
+		return hexes;
+	}
+	else
+	{
+		return position.neighbouringTiles();
+	}
+}
+
 std::vector<si32> CStack::activeSpells() const
 {
 	std::vector<si32> ret;

+ 2 - 0
lib/BattleState.h

@@ -88,6 +88,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
 	TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, 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)
 	std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, THex destinationTile); //calculates stack affected by given spell
+	std::set<CStack*> getAttackedCreatures(const CStack* attacker, THex destinationTile); //calculates range of multi-hex attacks
 	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
 	CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
 	CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
@@ -185,6 +186,7 @@ public:
 	THex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
 	std::vector<THex> getHexes() const; //up to two occupied hexes, starting from front
 	bool coversPos(THex position) const; //checks also if unit is double-wide
+	std::vector<THex> getSurroundingHexes() const; // get six or 8 surrounding hexes depending on creature size
 
 	void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled
 

+ 1 - 1
lib/NetPacks.h

@@ -1304,7 +1304,7 @@ struct BattleAttack : public CPackForClient//3006
 
 	std::vector<BattleStackAttacked> bsa;
 	ui32 stackAttacking;
-	ui8 flags; //usues Eflags (below)
+	ui8 flags; //uses Eflags (below)
 	enum EFlags{SHOT = 1, COUNTER = 2, LUCKY = 4, UNLUCKY = 8, BALLISTA_DOUBLE_DMG = 16};
 
 	bool shot() const//distance attack - decrease number of shots

+ 60 - 82
server/CGameHandler.cpp

@@ -508,10 +508,6 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 {
 	bat.bsa.clear();
 	bat.stackAttacking = att->ID;
-	bat.bsa.push_back(BattleStackAttacked());
-	BattleStackAttacked *bsa = &bat.bsa.back();
-	bsa->stackAttacked = def->ID;
-	bsa->attackerID = att->ID;
 	int attackerLuck = att->LuckVal();
 	const CGHeroInstance * h0 = gs->curB->heroes[0],
 		* h1 = gs->curB->heroes[1];
@@ -537,19 +533,29 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 			bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
 		}
 	}
+	// only primary target 
+	applyBattleEffects(bat, att, def, distance, false);
 
-	bsa->damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg());//counting dealt damage
-	int dmg = bsa->damageAmount;
-	def->prepareAttacked(*bsa);
+	if (!bat.shot()) //multiple-hex attack - only in meele
+	{
+		std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, def->position);
+		//TODO: get exact attacked hex for defender
+
+		BOOST_FOREACH(CStack * stack, attackedCreatures)
+		{
+			if (stack != def) //do not hit same stack twice
+			{
+				applyBattleEffects(bat, att, stack, distance, true);
+			}
+		}
+	}
 
-	//TODO: multiple attack targets
 	const Bonus * bonus = att->getBonus(Selector::type(Bonus::SPELL_LIKE_ATTACK));
-	if (bonus)
+	if (bonus && (bat.shot())) //TODO: make it work in meele?
 	{
-		bsa->flags |= BattleStackAttacked::EFFECT;
-		bsa->effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other 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?
 
-		BattleStackAttacked bss = *bsa; // copy some parameters, such as attacker
 		std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, def->position);
 		//TODO: get exact attacked hex for defender
 
@@ -557,82 +563,54 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 		{
 			if (stack != def) //do not hit same stack twice
 			{
-					bss.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
-				bss.stackAttacked = stack->ID;
-				bss.damageAmount = gs->curB->calculateDmg(att, stack, gs->curB->battleGetOwner(att),
-					gs->curB->battleGetOwner(stack), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg());
-				bat.bsa.push_back(bss); //add this stack to the list of victims
-				stack->prepareAttacked(bss);
-
-				//life drain handling
-				if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && stack->isLiving())
-				{
-					StacksHealedOrResurrected shi;
-					shi.lifeDrain = (ui8)true;
-					shi.tentHealing = (ui8)false;
-					shi.drainedFrom = stack->ID;
-
-					StacksHealedOrResurrected::HealInfo hi;
-					hi.stackID = stack->ID;
-					hi.healedHP = std::min<int>(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) );
-					hi.lowLevelResurrection = false;
-					shi.healedStacks.push_back(hi);
-
-					if (hi.healedHP > 0)
-					{
-						bsa->healedStacks.push_back(shi);
-					}
-				} 
-
-				//fire shield handling
-				if (!bat.shot() && stack->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa->killed() )
-				{
-					bss.stackAttacked = att->ID; //invert
-					bss.attackerID = stack->ID;
-					bss.flags = BattleStackAttacked::EFFECT; //not seondary - fire shield damages only one creature
-					bss.effect = 11;
-
-					bsa->damageAmount = (dmg * stack->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
-					att->prepareAttacked(*bsa);
-				}
+				applyBattleEffects(bat, att, stack, distance, true);
 			}
 		}
 	}
-	else // only one target 
-	{ // TODO: move duplicated code to separate function
-		//life drain handling
-		if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
-		{
-			StacksHealedOrResurrected shi;
-			shi.lifeDrain = (ui8)true;
-			shi.tentHealing = (ui8)false;
-			shi.drainedFrom = def->ID;
-
-			StacksHealedOrResurrected::HealInfo hi;
-			hi.stackID = att->ID;
-			hi.healedHP = std::min<int>(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) );
-			hi.lowLevelResurrection = false;
-			shi.healedStacks.push_back(hi);
-
-			if (hi.healedHP > 0)
-			{
-				bsa->healedStacks.push_back(shi);
-			}
-		} 
+}
+void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, const CStack *def, int distance, bool secondary) //helper function for prepareAttack
+{
+	BattleStackAttacked bsa;
+	if (secondary)
+		bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
+	bsa.attackerID = att->ID;
+	bsa.stackAttacked = def->ID;
+	bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg());
+	def->prepareAttacked(bsa); //calculate casualties
+	bat.bsa.push_back(bsa); //add this stack to the list of victims
+
+	//life drain handling
+	if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
+	{
+		StacksHealedOrResurrected shi;
+		shi.lifeDrain = (ui8)true;
+		shi.tentHealing = (ui8)false;
+		shi.drainedFrom = def->ID;
+
+		StacksHealedOrResurrected::HealInfo hi;
+		hi.stackID = att->ID;
+		hi.healedHP = std::min<int>(bsa.damageAmount, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) );
+		hi.lowLevelResurrection = false;
+		shi.healedStacks.push_back(hi);
 
-		//fire shield handling
-		if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa->killed() )
+		if (hi.healedHP > 0)
 		{
-			bat.bsa.push_back(BattleStackAttacked());
-			BattleStackAttacked *bsa = &bat.bsa.back();
-			bsa->stackAttacked = att->ID;
-			bsa->attackerID = def->ID;
-			bsa->flags |= BattleStackAttacked::EFFECT;
-			bsa->effect = 11;
-
-			bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
-			att->prepareAttacked(*bsa);
+			bsa.healedStacks.push_back(shi);
 		}
+	} 
+
+	//fire shield handling
+	if (!bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa.killed() )
+	{
+		BattleStackAttacked bsa;
+		bsa.stackAttacked = att->ID; //invert
+		bsa.attackerID = def->ID;
+		bsa.flags |= BattleStackAttacked::EFFECT;
+		bsa.effect = 11;
+
+		bsa.damageAmount = (bsa.damageAmount * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
+		att->prepareAttacked(bsa);
+		bat.bsa.push_back(bsa);
 	}
 }
 void CGameHandler::handleConnection(std::set<int> players, CConnection &c)

+ 1 - 0
server/CGameHandler.h

@@ -118,6 +118,7 @@ public:
 	//
 	void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
 	void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
+	void applyBattleEffects(BattleAttack &bat, const CStack *att, const CStack *def, int distance, bool secondary); //damage, drain life & fire shield
 	void checkForBattleEnd( std::vector<CStack*> &stacks );
 	void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
 	void setBattleResult(int resultType, int victoriusSide);