1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00
* 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
This commit is contained in:
Michał W. Urbańczyk 2012-02-16 21:19:07 +00:00
parent 0c3e88226f
commit 4baf4e13ed
11 changed files with 287 additions and 157 deletions

View File

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

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

View File

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

View File

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

View File

@ -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 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) for(int it=0; it<stacks.size(); ++it)
{ {
if((s->id == 24 && !stacks[it]->getCreature()->isUndead()) //death ripple if((s->id == Spells::DEATH_RIPPLE && !stacks[it]->getCreature()->isUndead()) //death ripple
|| (s->id == 25 && stacks[it]->getCreature()->isUndead()) //destroy undead || (s->id == Spells::DESTROY_UNDEAD && stacks[it]->getCreature()->isUndead()) //destroy undead
|| (s->id == 26) //Armageddon || (s->id == Spells::ARMAGEDDON) //Armageddon
) )
{ {
if(stacks[it]->alive()) if(stacks[it]->isValidTarget())
attackedCres.insert(stacks[it]); 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) 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 it's non negative spell and our unit or non positive spell and hostile unit */
if((s->positiveness >= 0 && stacks[it]->owner == attackerOwner) if((!s->isNegative() && stacks[it]->owner == attackerOwner)
||(s->positiveness <= 0 && stacks[it]->owner != attackerOwner ) ||(!s->isPositive() && stacks[it]->owner != attackerOwner )
) )
{ {
if(!onlyAlive || stacks[it]->alive()) if(stacks[it]->isValidTarget(!onlyAlive))
attackedCres.insert(stacks[it]); attackedCres.insert(stacks[it]);
} }
} }
@ -1889,7 +1889,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int pla
{ {
switch (spell->positiveness) switch (spell->positiveness)
{ {
case 1: case CSpell::POSITIVE:
if(stack->owner == caster->getOwner()) if(stack->owner == caster->getOwner())
{ {
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK) if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
@ -1899,14 +1899,14 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int pla
} }
} }
break; break;
case 0: case CSpell::NEUTRAL:
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK) if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
{ {
targetExists = true; targetExists = true;
break; break;
} }
break; break;
case -1: case CSpell::NEGATIVE:
if(stack->owner != caster->getOwner()) if(stack->owner != caster->getOwner())
{ {
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK) 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 for (int i = 0; i < GameConstants::SPELLS_QUANTITY; ++i) //should not use future spells added by mods
{ {
spell = VLC->spellh->spells[i]; spell = VLC->spellh->spells[i];
if (spell->positiveness == 1) //only positive if (spell->isPositive()) //only positive
{ {
if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, i) || if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, i) ||
battleCanCastThisSpellHere(subject->owner, spell, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK) 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 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)) if ((damageSpell && subject->hasBonusOfType(element, 2)) || subject->hasBonusOfType(element, 1))
return true; return true;
} }
else if (spell->positiveness == 1) //positive else if (spell->isPositive()) //positive
{ {
if (subject->hasBonusOfType(element, 0)) //must be immune to all spells if (subject->hasBonusOfType(element, 0)) //must be immune to all spells
return true; return true;
@ -2098,7 +2098,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInst
const CStack * subject = getStackT(dest, false); const CStack * subject = getStackT(dest, false);
if(subject) 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; return ESpellCastProblem::OK;
switch (spell->id) //TODO: more general logic for new spells? switch (spell->id) //TODO: more general logic for new spells?
@ -2132,7 +2132,7 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInst
bool hasPositiveSpell = false; bool hasPositiveSpell = false;
BOOST_FOREACH(const Bonus * b, *spellBon) BOOST_FOREACH(const Bonus * b, *spellBon)
{ {
if(VLC->spellh->spells[b->sid]->positiveness > 0) if(VLC->spellh->spells[b->sid]->isPositive())
{ {
hasPositiveSpell = true; hasPositiveSpell = true;
break; 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 //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; continue;
/* /*
@ -2811,6 +2811,11 @@ std::string CStack::getName() const
return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base 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 ) bool CMP_stack::operator()( const CStack* a, const CStack* b )
{ {
switch(phase) switch(phase)

View File

@ -250,6 +250,7 @@ public:
{ {
return vstd::contains(state,EBattleStackState::ALIVE); 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 class DLL_LINKAGE CMP_stack

View File

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

View File

@ -18,6 +18,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};
ui32 id; ui32 id;
std::string name; std::string name;
std::string abbName; //abbreviated name std::string abbName; //abbreviated name
@ -41,6 +42,9 @@ public:
si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
ETargetType getTargetType() const; ETargetType getTargetType() const;
bool isPositive() const;
bool isNegative() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs h & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs

View File

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

View File

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

View File

@ -3579,10 +3579,10 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
BOOST_FOREACH (CStack * stack, gs->curB->stacks) BOOST_FOREACH (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->positiveness >= 0 && stack->owner == casterColor) if((!spell->isNegative() && stack->owner == casterColor)
||(spell->positiveness <= 0 && 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); attackedCres.insert(stack);
} }
} }
@ -3967,7 +3967,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
} }
//Magic Mirror effect //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) 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) 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; attack.destinationTile = gs->curB->stacks[g]->position;
break; break;
@ -5412,7 +5412,7 @@ void CGameHandler::runBattle()
for (int v=0; v<gs->curB->stacks.size(); ++v) for (int v=0; v<gs->curB->stacks.size(); ++v)
{ {
const CStack * cstack = gs->curB->stacks[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)) if (cstack->hasBonusOfType(Bonus::SIEGE_WEAPON))
secondPriority.push_back(cstack); secondPriority.push_back(cstack);