From 4baf4e13ed2b1f5177895d10a7ec13548762367a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Thu, 16 Feb 2012 21:19:07 +0000 Subject: [PATCH] * 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 --- client/BattleInterface/CBattleInterface.cpp | 47 ++- client/BattleInterface/CBattleInterface.h | 3 +- client/CPlayerInterface.cpp | 23 +- client/VCMI_client.vcxproj.filters | 301 ++++++++++++++------ lib/BattleState.cpp | 39 +-- lib/BattleState.h | 1 + lib/CSpellHandler.cpp | 10 + lib/CSpellHandler.h | 4 + lib/HeroBonus.cpp | 2 +- lib/NetPacksLib.cpp | 2 +- server/CGameHandler.cpp | 12 +- 11 files changed, 287 insertions(+), 157 deletions(-) diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index eb3571add..d00b6dff6 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/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) diff --git a/client/BattleInterface/CBattleInterface.h b/client/BattleInterface/CBattleInterface.h index 8a0971aee..a1705e646 100644 --- a/client/BattleInterface/CBattleInterface.h +++ b/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; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index c70d8b037..40afce2fa 100644 --- a/client/CPlayerInterface.cpp +++ b/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 un(*pim); @@ -2345,8 +2340,24 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) void CPlayerInterface::playerStartsTurn(ui8 player) { + { + boost::unique_lock 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 un(*pim); + adventureInt->minimap.redraw(); adventureInt->infoBar.enemyTurn(player, 0.5); diff --git a/client/VCMI_client.vcxproj.filters b/client/VCMI_client.vcxproj.filters index e8f18ab62..e06c1ce19 100644 --- a/client/VCMI_client.vcxproj.filters +++ b/client/VCMI_client.vcxproj.filters @@ -1,136 +1,247 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - UIFramework + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files - - UIFramework + + Source Files - - UIFramework + + Source Files - - UIFramework + + Source Files - - UIFramework + + Source Files - - UIFramework + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files - BattleInterface + Source Files - BattleInterface + Source Files - BattleInterface + Source Files - BattleInterface + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UIFramework + + Header Files - - UIFramework + + Header Files - - UIFramework + + Header Files - - UIFramework + + Header Files - - UIFramework + + Header Files - - UIFramework + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files - BattleInterface + Header Files - BattleInterface + Header Files - BattleInterface + Header Files - BattleInterface + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files - - - + - - {3b4624b5-80f2-4bd8-b4c0-af29d3be4b58} - - - {918a73d4-7386-4d29-9515-a3579051d4ac} - + \ No newline at end of file diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index bad0df223..d71df6586 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -728,16 +728,16 @@ std::set 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; itid == 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 BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe for(int it=0; itpositiveness >= 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 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) diff --git a/lib/BattleState.h b/lib/BattleState.h index 3301072c0..e6f76a0f3 100644 --- a/lib/BattleState.h +++ b/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 diff --git a/lib/CSpellHandler.cpp b/lib/CSpellHandler.cpp index 71d586c0f..709f6378d 100644 --- a/lib/CSpellHandler.cpp +++ b/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'; diff --git a/lib/CSpellHandler.h b/lib/CSpellHandler.h index d0c3dffc1..0b307c820 100644 --- a/lib/CSpellHandler.h +++ b/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 void serialize(Handler &h, const int version) { h & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 4879eac59..a60c271ce 100644 --- a/lib/HeroBonus.cpp +++ b/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 } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 126fd623d..9d08bb817 100644 --- a/lib/NetPacksLib.cpp +++ b/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); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index fa174c7d5..1198efd94 100644 --- a/server/CGameHandler.cpp +++ b/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::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) { @@ -5376,7 +5376,7 @@ void CGameHandler::runBattle() for(int g=0; gcurB->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; vcurB->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);