Explorar o código

Partial support for Death Cloud & Magog's Fire Ball.

Lots of refactoring is needed to handle this and multiple-target attacks.
DjWarmonger %!s(int64=14) %!d(string=hai) anos
pai
achega
51943e5f1e
Modificáronse 5 ficheiros con 102 adicións e 39 borrados
  1. 2 2
      client/CHeroWindow.cpp
  2. 11 1
      lib/BattleState.cpp
  3. 1 1
      lib/HeroBonus.h
  4. 5 1
      lib/NetPacks.h
  5. 83 34
      server/CGameHandler.cpp

+ 2 - 2
client/CHeroWindow.cpp

@@ -6,8 +6,8 @@
 #include "CHeroWindow.h"
 #include "CMessage.h"
 #include "CKingdomInterface.h"
-#include <SDL.h>
-#include <SDL_Extensions.h>
+#include "SDL.h"
+#include "SDL_Extensions.h"
 #include "CBitmapHandler.h"
 #include "Graphics.h"
 #include "CSpellWindow.h"

+ 11 - 1
lib/BattleState.cpp

@@ -710,7 +710,17 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
 		{
 			CStack * st = getStackT(*it, onlyAlive);
 			if(st)
-				attackedCres.insert(st);
+			{
+				if (s->id == 76) //Death Cloud //TODO: fireball and fire immunity
+				{
+					if (st->isLiving() || st->position == destinationTile) //directly hit or alive
+					{
+						attackedCres.insert(st);
+					}
+				}
+				else
+					attackedCres.insert(st);
+			}
 		}
 	}
 	else if(s->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)

+ 1 - 1
lib/HeroBonus.h

@@ -101,7 +101,7 @@ namespace PrimarySkill
 	BONUS_NAME(BLOCKS_RETALIATION) /*eg. naga*/			\
 	BONUS_NAME(SPELL_IMMUNITY) /*subid - spell id*/		\
 	BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
-	BONUS_NAME(SPELL_LIKE_ATTACK) /*value - spell id; range is taken from spell, but damage from creature; eg. magog*/ \
+	BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
 	BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/	\
 	BONUS_NAME(DAEMON_SUMMONING) /*pit lord*/			\
 	BONUS_NAME(FIRE_IMMUNITY)	/*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/						\

+ 5 - 1
lib/NetPacks.h

@@ -1262,7 +1262,7 @@ struct BattleStackAttacked : public CPackForClient//3005
 
 	ui32 stackAttacked, attackerID;
 	ui32 newAmount, newHP, killedAmount, damageAmount;
-	enum EFlags {KILLED = 1, EFFECT = 2};
+	enum EFlags {KILLED = 1, EFFECT = 2, SECONDARY = 4};
 	ui8 flags; //uses EFlags (above)
 	ui32 effect; //set only if flag EFFECT is set
 	std::vector<StacksHealedOrResurrected> healedStacks; //used when life drain
@@ -1276,6 +1276,10 @@ struct BattleStackAttacked : public CPackForClient//3005
 	{
 		return flags & EFFECT;
 	}
+	bool isSecondary() const//if stack was not a primary target (receives no spell effects)
+	{
+		return flags & SECONDARY;
+	}
 	bool lifeDrain() const //if this attack involves life drain effect
 	{
 		return healedStacks.size() > 0;

+ 83 - 34
server/CGameHandler.cpp

@@ -539,48 +539,97 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	}
 
 	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);
 
-	//life drain handling
-	if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
+	//TODO: multiple attack targets
+	const Bonus * bonus = att->getBonus(Selector::type(Bonus::SPELL_LIKE_ATTACK));
+	if (bonus)
 	{
-		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);
+		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);
 
-		if (hi.healedHP > 0)
+		BOOST_FOREACH(CStack * stack, attackedCreatures)
 		{
-			bsa->healedStacks.push_back(shi);
-		}
-	} 
-	else
-	{
-	}
+			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() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !bsa->killed() )
-	{
-		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;
+				//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 * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
-		att->prepareAttacked(*bsa);
+					bsa->damageAmount = (dmg * stack->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
+					att->prepareAttacked(*bsa);
+				}
+			}
+		}
 	}
+	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);
+			}
+		} 
 
+		//fire shield handling
+		if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa->killed() )
+		{
+			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);
+		}
+	}
 }
 void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 {
@@ -4273,9 +4322,9 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 		BOOST_FOREACH(ui32 spellID, spellsToCast)
 		{
 			const CStack * oneOfAttacked = NULL;
-			for(int g=0; g<bat.bsa.size(); ++g)
+			for (int g=0; g<bat.bsa.size(); ++g)
 			{
-				if (bat.bsa[g].newAmount > 0)
+				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);
 					break;