Quellcode durchsuchen

* fixed #857 and #858
* fixed crash when AI attacked player before his first turn
* fixed various crashes when mass-effect spells affected town turrets in sieges
* some refactoring around spell positiveness

Michał W. Urbańczyk vor 13 Jahren
Ursprung
Commit
4baf4e13ed

+ 17 - 30
client/BattleInterface/CBattleInterface.cpp

@@ -949,7 +949,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 							const CSpell * spell =  CGI->spellh->spells[creatureSpellToCast];
 							if (curInt->cb->battleCanCastThisSpell(spell, BattleHex(myNumber)) == ESpellCastProblem::OK)
 							{
-								if ((spell->positiveness > -1 && ourStack) || (spell->positiveness < 1 && !ourStack))
+								if ((!spell->isNegative() && ourStack) || (!spell->isPositive() && !ourStack))
 								{
 									CCS->curh->changeGraphic(3, 0);
 									stackCastsSpell = true;
@@ -1788,7 +1788,7 @@ void CBattleInterface::hexLclicked(int whichOne)
 						const CSpell * spell =  CGI->spellh->spells[creatureSpellToCast];
 						if (curInt->cb->battleCanCastThisSpell(spell, BattleHex(whichOne)) == ESpellCastProblem::OK)
 						{
-							if ((spell->positiveness > -1 && ourStack) || (spell->positiveness < 1 && !ourStack))
+							if ((!spell->isNegative() && ourStack) || (!spell->isPositive() && !ourStack))
 							{
 								giveCommand(BattleAction::MONSTER_SPELL, whichOne, actSt->ID, creatureSpellToCast);
 								spellCast = true;
@@ -2371,40 +2371,14 @@ void CBattleInterface::castThisSpell(int spellID)
 	spellSelMode = ANY_LOCATION;
 	if(spell.getTargetType() == CSpell::CREATURE)
 	{
-		switch(spell.positiveness)
-		{
-		case -1 :
-			spellSelMode = HOSTILE_CREATURE;
-			break;
-		case 0:
-			spellSelMode = ANY_CREATURE;
-			break;
-		case 1:
-			spellSelMode = FRIENDLY_CREATURE;
-			break;
-		}
+		spellSelMode = selectionTypeByPositiveness(spell);
 	}
 	if(spell.getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)
 	{
 		if(castingHero && castingHero->getSpellSchoolLevel(&spell) < 3)
-		{
-			switch(spell.positiveness)
-			{
-			case -1 :
-				spellSelMode = HOSTILE_CREATURE;
-				break;
-			case 0:
-				spellSelMode = ANY_CREATURE;
-				break;
-			case 1:
-				spellSelMode = FRIENDLY_CREATURE;
-				break;
-			}
-		}
+			spellSelMode = selectionTypeByPositiveness(spell);
 		else
-		{
 			spellSelMode = NO_LOCATION;
-		}
 	}
 	if(spell.getTargetType() == CSpell::OBSTACLE)
 	{
@@ -3088,6 +3062,19 @@ void CBattleInterface::bTacticNextStack()
 		stackActivated(stacksOfMine.front());
 }
 
+CBattleInterface::SpellSelectionType CBattleInterface::selectionTypeByPositiveness(const CSpell & spell)
+{
+	switch(spell.positiveness)
+	{
+	case CSpell::NEGATIVE :
+		return HOSTILE_CREATURE;
+	case CSpell::NEUTRAL:
+		return ANY_CREATURE;
+	case CSpell::POSITIVE:
+		return FRIENDLY_CREATURE;
+	}
+}
+
 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)

+ 2 - 1
client/BattleInterface/CBattleInterface.h

@@ -246,7 +246,8 @@ public:
 	void endAction(const BattleAction* action);
 	void hideQueue();
 	void showQueue();
-	
+	SpellSelectionType selectionTypeByPositiveness(const CSpell & spell);
+
 	
 	friend class CPlayerInterface;
 	friend class CAdventureMapButton;

+ 17 - 6
client/CPlayerInterface.cpp

@@ -157,12 +157,6 @@ void CPlayerInterface::yourTurn()
 
 			autosaveCount = getLastIndex("Autosave_");
 
-			if(!GH.listInt.size())
-			{
-				GH.pushInt(adventureInt);
-				adventureInt->activateKeys();
-			}
-
 			if(firstCall > 0) //new game, not loaded
 			{
 				int index = getLastIndex("Newgame_Autosave_");
@@ -224,6 +218,7 @@ STRONG_INLINE void delObjRect(const int & x, const int & y, const int & z, const
 }
 void CPlayerInterface::heroMoved(const TryMoveHero & details)
 {
+	waitWhileDialog();
 	if(LOCPLINT != this)
 		return;
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
@@ -2345,8 +2340,24 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
 
 void CPlayerInterface::playerStartsTurn(ui8 player)
 {
+	{
+		boost::unique_lock<boost::recursive_mutex> un(*pim);
+		if(!GH.listInt.size())
+		{
+			GH.pushInt(adventureInt);
+			adventureInt->activateKeys();
+		}
+		if(howManyPeople == 1)
+		{
+			GH.curInt = this;
+			adventureInt->startTurn();
+		}
+	}
 	if(player != playerID && this == LOCPLINT)
 	{
+		waitWhileDialog();
+		boost::unique_lock<boost::recursive_mutex> un(*pim);
+
 		adventureInt->minimap.redraw();
 		adventureInt->infoBar.enemyTurn(player, 0.5);
 

+ 206 - 95
client/VCMI_client.vcxproj.filters

@@ -1,136 +1,247 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="CAdvmapInterface.cpp" />
-    <ClCompile Include="CAnimation.cpp" />
-    <ClCompile Include="..\CCallback.cpp" />
-    <ClCompile Include="CBitmapHandler.cpp" />
-    <ClCompile Include="CCastleInterface.cpp" />
-    <ClCompile Include="CConfigHandler.cpp" />
-    <ClCompile Include="CCreatureWindow.cpp" />
-    <ClCompile Include="CDefHandler.cpp" />
-    <ClCompile Include="CGameInfo.cpp" />
-    <ClCompile Include="CHeroWindow.cpp" />
-    <ClCompile Include="CKingdomInterface.cpp" />
-    <ClCompile Include="Client.cpp" />
-    <ClCompile Include="CMessage.cpp" />
-    <ClCompile Include="CMT.cpp" />
-    <ClCompile Include="CMusicHandler.cpp" />
-    <ClCompile Include="CPlayerInterface.cpp" />
-    <ClCompile Include="CPreGame.cpp" />
-    <ClCompile Include="CSndHandler.cpp" />
-    <ClCompile Include="CSpellWindow.cpp" />
-    <ClCompile Include="CVideoHandler.cpp" />
-    <ClCompile Include="Graphics.cpp" />
-    <ClCompile Include="GUIClasses.cpp" />
-    <ClCompile Include="mapHandler.cpp" />
-    <ClCompile Include="NetPacksClient.cpp" />
-    <ClCompile Include="StdInc.cpp" />
-    <ClCompile Include="UIFramework\CCursorHandler.cpp">
-      <Filter>UIFramework</Filter>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="CAdvmapInterface.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="UIFramework\CGuiHandler.cpp">
-      <Filter>UIFramework</Filter>
+    <ClCompile Include="CBitmapHandler.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="UIFramework\CIntObject.cpp">
-      <Filter>UIFramework</Filter>
+    <ClCompile Include="..\CCallback.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="UIFramework\CIntObjectClasses.cpp">
-      <Filter>UIFramework</Filter>
+    <ClCompile Include="CCastleInterface.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="UIFramework\Geometries.cpp">
-      <Filter>UIFramework</Filter>
+    <ClCompile Include="CConfigHandler.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="UIFramework\SDL_Extensions.cpp">
-      <Filter>UIFramework</Filter>
+    <ClCompile Include="CGameInfo.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CHeroWindow.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CKingdomInterface.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Client.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CMessage.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CMT.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CPlayerInterface.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CPreGame.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CSpellWindow.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Graphics.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="GUIClasses.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="NetPacksClient.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="mapHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CAnimation.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CDefHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CMusicHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CSndHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CVideoHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CCreatureWindow.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="BattleInterface\CBattleAnimations.cpp">
-      <Filter>BattleInterface</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="BattleInterface\CBattleInterface.cpp">
-      <Filter>BattleInterface</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="BattleInterface\CBattleInterfaceClasses.cpp">
-      <Filter>BattleInterface</Filter>
+      <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="BattleInterface\CCreatureAnimation.cpp">
-      <Filter>BattleInterface</Filter>
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="StdInc.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\CCursorHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\CGuiHandler.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\CIntObject.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\CIntObjectClasses.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\Geometries.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="UIFramework\SDL_Extensions.cpp">
+      <Filter>Source Files</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="CAdvmapInterface.h" />
-    <ClInclude Include="CAnimation.h" />
-    <ClInclude Include="CBitmapHandler.h" />
-    <ClInclude Include="..\CCallback.h" />
-    <ClInclude Include="CCastleInterface.h" />
-    <ClInclude Include="CConfigHandler.h" />
-    <ClInclude Include="CCreatureWindow.h" />
-    <ClInclude Include="CDefHandler.h" />
-    <ClInclude Include="CGameInfo.h" />
-    <ClInclude Include="CHeroWindow.h" />
-    <ClInclude Include="CKingdomInterface.h" />
-    <ClInclude Include="Client.h" />
-    <ClInclude Include="CMessage.h" />
-    <ClInclude Include="CMusicBase.h" />
-    <ClInclude Include="CMusicHandler.h" />
-    <ClInclude Include="CPlayerInterface.h" />
-    <ClInclude Include="CPreGame.h" />
-    <ClInclude Include="CSndHandler.h" />
-    <ClInclude Include="CSoundBase.h" />
-    <ClInclude Include="CSpellWindow.h" />
-    <ClInclude Include="CVideoHandler.h" />
-    <ClInclude Include="FontBase.h" />
-    <ClInclude Include="FunctionList.h" />
-    <ClInclude Include="Graphics.h" />
-    <ClInclude Include="GUIClasses.h" />
-    <ClInclude Include="mapHandler.h" />
-    <ClInclude Include="resource.h" />
-    <ClInclude Include="StdInc.h" />
-    <ClInclude Include="..\Global.h" />
-    <ClInclude Include="UIFramework\CCursorHandler.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="CAdvmapInterface.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="UIFramework\CGuiHandler.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="CBitmapHandler.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="UIFramework\CIntObject.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="..\CCallback.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="UIFramework\CIntObjectClasses.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="CCastleInterface.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="UIFramework\Geometries.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="CConfigHandler.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="UIFramework\SDL_Extensions.h">
-      <Filter>UIFramework</Filter>
+    <ClInclude Include="CGameInfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CHeroWindow.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CKingdomInterface.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Client.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CMessage.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CPlayerInterface.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CPreGame.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CSpellWindow.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="FontBase.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="FunctionList.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\global.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Graphics.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="GUIClasses.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="mapHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CAnimation.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CDefHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CMusicBase.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CMusicHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CSndHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CSoundBase.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CVideoHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CCreatureWindow.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="BattleInterface\CBattleAnimations.h">
-      <Filter>BattleInterface</Filter>
+      <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="BattleInterface\CBattleInterface.h">
-      <Filter>BattleInterface</Filter>
+      <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="BattleInterface\CBattleInterfaceClasses.h">
-      <Filter>BattleInterface</Filter>
+      <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="BattleInterface\CCreatureAnimation.h">
-      <Filter>BattleInterface</Filter>
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="StdInc.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\CCursorHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\CGuiHandler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\CIntObject.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\CIntObjectClasses.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\Geometries.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="UIFramework\SDL_Extensions.h">
+      <Filter>Header Files</Filter>
     </ClInclude>
-  </ItemGroup>
-  <ItemGroup>
-    <ResourceCompile Include="VCMI_client.rc" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\ChangeLog" />
     <None Include="vcmi.ico" />
+    <None Include="ClassDiagram21.cd" />
   </ItemGroup>
   <ItemGroup>
-    <Filter Include="BattleInterface">
-      <UniqueIdentifier>{3b4624b5-80f2-4bd8-b4c0-af29d3be4b58}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="UIFramework">
-      <UniqueIdentifier>{918a73d4-7386-4d29-9515-a3579051d4ac}</UniqueIdentifier>
-    </Filter>
+    <ResourceCompile Include="VCMI_client.rc" />
   </ItemGroup>
 </Project>

+ 22 - 17
lib/BattleState.cpp

@@ -728,16 +728,16 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe
 
 	bool onlyAlive = s->id != 38 && s->id != 39; //when casting resurrection or animate dead we should be allow to select dead stack
 
-	if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and Armageddon
+	if(s->id == Spells::DEATH_RIPPLE || s->id == Spells::DESTROY_UNDEAD || s->id == Spells::ARMAGEDDON)
 	{
 		for(int it=0; it<stacks.size(); ++it)
 		{
-			if((s->id == 24 && !stacks[it]->getCreature()->isUndead()) //death ripple
-				|| (s->id == 25 && stacks[it]->getCreature()->isUndead()) //destroy undead
-				|| (s->id == 26) //Armageddon
+			if((s->id == Spells::DEATH_RIPPLE && !stacks[it]->getCreature()->isUndead()) //death ripple
+				|| (s->id == Spells::DESTROY_UNDEAD && stacks[it]->getCreature()->isUndead()) //destroy undead
+				|| (s->id == Spells::ARMAGEDDON) //Armageddon
 				)
 			{
-				if(stacks[it]->alive())
+				if(stacks[it]->isValidTarget())
 					attackedCres.insert(stacks[it]);
 			}
 		}
@@ -774,11 +774,11 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe
 			for(int it=0; it<stacks.size(); ++it)
 			{
 				/*if it's non negative spell and our unit or non positive spell and hostile unit */
-				if((s->positiveness >= 0 && stacks[it]->owner == attackerOwner)
-					||(s->positiveness <= 0 && stacks[it]->owner != attackerOwner )
+				if((!s->isNegative() && stacks[it]->owner == attackerOwner)
+					||(!s->isPositive() && stacks[it]->owner != attackerOwner )
 					)
 				{
-					if(!onlyAlive || stacks[it]->alive())
+					if(stacks[it]->isValidTarget(!onlyAlive))
 						attackedCres.insert(stacks[it]);
 				}
 			}
@@ -1889,7 +1889,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int pla
 			{
 				switch (spell->positiveness)
 				{
-				case 1:
+				case CSpell::POSITIVE:
 					if(stack->owner == caster->getOwner())
 					{
 						if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
@@ -1899,14 +1899,14 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int pla
 						}
 					}
 					break;
-				case 0:
+				case CSpell::NEUTRAL:
 					if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
 					{
 						targetExists = true;
 						break;
 					}
 					break;
-				case -1:
+				case CSpell::NEGATIVE:
 					if(stack->owner != caster->getOwner())
 					{
 						if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
@@ -1950,7 +1950,7 @@ TSpell BattleInfo::getRandomBeneficialSpell(const CStack * subject) const
 	for (int i = 0; i < GameConstants::SPELLS_QUANTITY; ++i) //should not use future spells added by mods
 	{
 		spell = VLC->spellh->spells[i];
-		if (spell->positiveness == 1) //only positive
+		if (spell->isPositive()) //only positive
 		{
 			if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, i) ||
 				battleCanCastThisSpellHere(subject->owner, spell, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
@@ -2080,12 +2080,12 @@ bool NegateRemover(const Bonus* b)
 
 bool BattleInfo::battleTestElementalImmunity(const CStack * subject, const CSpell * spell, Bonus::BonusType element, bool damageSpell) const //helper for battleisImmune
 {
-	if (spell->positiveness < 1) //negative or indifferent
+	if (!spell->isPositive()) //negative or indifferent
 	{
 		if ((damageSpell && subject->hasBonusOfType(element, 2)) || subject->hasBonusOfType(element, 1))
 			return true;
 	}
-	else if (spell->positiveness == 1) //positive
+	else if (spell->isPositive()) //positive
 	{
 		if (subject->hasBonusOfType(element, 0)) //must be immune to all spells
 			return true;
@@ -2098,7 +2098,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInst
 	const CStack * subject = getStackT(dest, false);
 	if(subject)
 	{
-		if (spell->positiveness == 1 && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
+		if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
 			return ESpellCastProblem::OK;
 
 		switch (spell->id) //TODO: more general logic for new spells?
@@ -2132,7 +2132,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInst
 				bool hasPositiveSpell = false;
 				BOOST_FOREACH(const Bonus * b, *spellBon)
 				{
-					if(VLC->spellh->spells[b->sid]->positiveness > 0)
+					if(VLC->spellh->spells[b->sid]->isPositive())
 					{
 						hasPositiveSpell = true;
 						break;
@@ -2220,7 +2220,7 @@ std::vector<ui32> BattleInfo::calculateResistedStacks(const CSpell * sp, const C
 		}
 
 		//non-negative spells on friendly stacks should always succeed, unless immune
-		if(sp->positiveness >= 0 && (*it)->owner == casterSideOwner)
+		if(!sp->isNegative() && (*it)->owner == casterSideOwner)
 			continue;
 
 		/*
@@ -2811,6 +2811,11 @@ std::string CStack::getName() const
 	return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base
 }
 
+bool CStack::isValidTarget(bool allowDead/* = false*/) const /*alive non-turret stacks (can be attacked or be object of magic effect) */
+{
+	return (alive() || allowDead) && position.isValid();
+}
+
 bool CMP_stack::operator()( const CStack* a, const CStack* b )
 {
 	switch(phase)

+ 1 - 0
lib/BattleState.h

@@ -250,6 +250,7 @@ public:
 	{
 		return vstd::contains(state,EBattleStackState::ALIVE);
 	}
+	bool isValidTarget(bool allowDead = false) const; //alive non-turret stacks (can be attacked or be object of magic effect)
 };
 
 class DLL_LINKAGE CMP_stack

+ 10 - 0
lib/CSpellHandler.cpp

@@ -205,6 +205,16 @@ CSpell::ETargetType CSpell::getTargetType() const
 	return NO_TARGET;
 }
 
+bool CSpell::isPositive() const
+{
+	return positiveness == POSITIVE;
+}
+
+bool CSpell::isNegative() const
+{
+	return positiveness == NEGATIVE;
+}
+
 static bool startsWithX(const std::string &s)
 {
 	return s.size() && s[0] == 'x';

+ 4 - 0
lib/CSpellHandler.h

@@ -18,6 +18,7 @@ class DLL_LINKAGE CSpell
 {
 public:
 	enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
+	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
 	ui32 id;
 	std::string name;
 	std::string abbName; //abbreviated name
@@ -41,6 +42,9 @@ public:
 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
 	ETargetType getTargetType() const;
 
+	bool isPositive() const;
+	bool isNegative() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs 

+ 1 - 1
lib/HeroBonus.cpp

@@ -1030,7 +1030,7 @@ namespace Selector
 		if(b->source == Bonus::SPELL_EFFECT)
 		{
 			CSpell *sp = VLC->spellh->spells[b->sid];
-			return sp->positiveness == 1;
+			return sp->isPositive();
 		}
 		return false; //not a spell effect
 	}

+ 1 - 1
lib/NetPacksLib.cpp

@@ -1209,7 +1209,7 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs( CGameState *gs )
 			BOOST_FOREACH(Bonus *b, tmpFeatures)
 			{
 				const CSpell *s = b->sourceSpell();
-				if(s && s->positiveness < 0)
+				if(s && s->isNegative())
 				{
 					changedStack->removeBonus(b);
 				}

+ 6 - 6
server/CGameHandler.cpp

@@ -3579,10 +3579,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 		BOOST_FOREACH (CStack * stack, gs->curB->stacks)
 		{
 			/*if it's non negative spell and our unit or non positive spell and hostile unit */
-			if((spell->positiveness >= 0 && stack->owner == casterColor)
-				||(spell->positiveness <= 0 && stack->owner != casterColor ))
+			if((!spell->isNegative() && stack->owner == casterColor)
+				|| (!spell->isPositive() && stack->owner != casterColor))
 			{
-				if(stack->alive()) //TODO: allow dead targets somewhere in the future
+				if(stack->isValidTarget()) //TODO: allow dead targets somewhere in the future
 					attackedCres.insert(stack);
 			}
 		}
@@ -3967,7 +3967,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 	}
 
 	//Magic Mirror effect
-	if (spell->positiveness < 0 && 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)
 		{
@@ -5376,7 +5376,7 @@ void CGameHandler::runBattle()
 
 				for(int g=0; g<gs->curB->stacks.size(); ++g)
 				{
-					if(gs->curB->stacks[g]->owner != next->owner && gs->curB->stacks[g]->alive())
+					if(gs->curB->stacks[g]->owner != next->owner && gs->curB->stacks[g]->isValidTarget())
 					{
 						attack.destinationTile = gs->curB->stacks[g]->position;
 						break;
@@ -5412,7 +5412,7 @@ void CGameHandler::runBattle()
 				for (int v=0; v<gs->curB->stacks.size(); ++v)
 				{
 					const CStack * cstack = gs->curB->stacks[v];
-					if (cstack->owner == next->owner && cstack->firstHPleft < cstack->MaxHealth() && cstack->alive()) //it's friendly and not fully healthy
+					if (cstack->owner == next->owner && cstack->firstHPleft < cstack->MaxHealth() && cstack->isValidTarget()) //it's friendly and not fully healthy
 					{
 						if (cstack->hasBonusOfType(Bonus::SIEGE_WEAPON))
 							secondPriority.push_back(cstack);