diff --git a/AI/GeniusAI/BattleLogic.cpp b/AI/GeniusAI/BattleLogic.cpp index 383f87450..8859946af 100644 --- a/AI/GeniusAI/BattleLogic.cpp +++ b/AI/GeniusAI/BattleLogic.cpp @@ -300,7 +300,7 @@ BattleAction CBattleLogic::MakeDecision(int stackID) -std::vector CBattleLogic::GetAvailableHexesForAttacker(CStack *defender, CStack *attacker) +std::vector CBattleLogic::GetAvailableHexesForAttacker(const CStack *defender, const CStack *attacker) { int x = m_battleHelper.DecodeXPosition(defender->position); int y = m_battleHelper.DecodeYPosition(defender->position); @@ -408,7 +408,7 @@ std::vector CBattleLogic::GetAvailableHexesForAttacker(CStack *defender, CS } int new_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second); - CStack *st = m_cb->battleGetStackByPos(new_pos); + const CStack *st = m_cb->battleGetStackByPos(new_pos); if (st == NULL || st->amount < 1) { @@ -479,7 +479,7 @@ BattleAction CBattleLogic::MakeWait(int stackID) BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID) { - CStack *attackerStack = m_cb->battleGetStackByID(attackerID), + const CStack *attackerStack = m_cb->battleGetStackByID(attackerID), *destinationStack = m_cb->battleGetStackByID(destinationID); assert(attackerStack && destinationStack); @@ -744,7 +744,7 @@ void CBattleLogic::PrintBattleAction(const BattleAction &action) // for debug pu message += "stack - " + boost::lexical_cast(m_battleHelper.DecodeXPosition(action.additionalInfo)); message += ", " + boost::lexical_cast(m_battleHelper.DecodeYPosition(action.additionalInfo)); message += ", creature - "; - CStack *c = m_cb->battleGetStackByPos(action.additionalInfo); + const CStack *c = m_cb->battleGetStackByPos(action.additionalInfo); if (c && c->creature) { message += c->creature->nameRef; diff --git a/AI/GeniusAI/BattleLogic.h b/AI/GeniusAI/BattleLogic.h index 9ef9c4639..238bde8ac 100644 --- a/AI/GeniusAI/BattleLogic.h +++ b/AI/GeniusAI/BattleLogic.h @@ -105,7 +105,7 @@ private: /** * Helper function. It's used for performing an attack action. */ - std::vector GetAvailableHexesForAttacker(CStack *defender, CStack *attacker = NULL); + std::vector GetAvailableHexesForAttacker(const CStack *defender, const CStack *attacker = NULL); /** * Just make defend action. */ diff --git a/CCallback.cpp b/CCallback.cpp index e452e5ad0..fa657cd4e 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -442,7 +442,7 @@ int CCallback::battleGetStack(int pos) return gs->battleGetStack(pos); } -CStack* CCallback::battleGetStackByID(int ID) +const CStack* CCallback::battleGetStackByID(int ID) { boost::shared_lock lock(*gs->mx); if(!gs->curB) return NULL; @@ -456,7 +456,7 @@ int CCallback::battleMakeAction(BattleAction* action) return 0; } -CStack* CCallback::battleGetStackByPos(int pos) +const CStack* CCallback::battleGetStackByPos(int pos) { boost::shared_lock lock(*gs->mx); return battleGetStackByID(battleGetStack(pos)); @@ -553,7 +553,7 @@ bool CCallback::battleIsStackMine(int ID) bool CCallback::battleCanShoot(int ID, int dest) { boost::shared_lock lock(*gs->mx); - CStack *our = battleGetStackByID(ID), *dst = battleGetStackByPos(dest); + const CStack *our = battleGetStackByID(ID), *dst = battleGetStackByPos(dest); if(!our || !dst || !gs->curB) return false; //for(size_t g=0; geffects.size(); ++g) diff --git a/CCallback.h b/CCallback.h index 568ea45fe..c9f1338c0 100644 --- a/CCallback.h +++ b/CCallback.h @@ -166,8 +166,8 @@ public: virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield virtual std::vector battleGetAllObstacles()=0; //returns all obstacles on the battlefield virtual int battleGetStack(int pos)=0; //returns ID of stack on the tile - virtual CStack * battleGetStackByID(int ID)=0; //returns stack info by given ID - virtual CStack * battleGetStackByPos(int pos)=0; //returns stack info by given pos + virtual const CStack * battleGetStackByID(int ID)=0; //returns stack info by given ID + virtual const CStack * battleGetStackByPos(int pos)=0; //returns stack info by given pos virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack virtual std::map battleGetStacks()=0; //returns stacks on battlefield @@ -267,8 +267,8 @@ public: int battleGetObstaclesAtTile(int tile); //returns bitfield std::vector battleGetAllObstacles(); //returns all obstacles on the battlefield int battleGetStack(int pos); //returns ID of stack on the tile - CStack * battleGetStackByID(int ID); //returns stack info by given ID - CStack * battleGetStackByPos(int pos); //returns stack info by given pos + const CStack * battleGetStackByID(int ID); //returns stack info by given ID + const CStack * battleGetStackByPos(int pos); //returns stack info by given pos int battleGetPos(int stack); //returns position (tile ID) of stack int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack std::map battleGetStacks(); //returns stacks on battlefield diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index d5c091920..a3c63b493 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -709,8 +709,8 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) { if(std::find(shadedHexes.begin(),shadedHexes.end(),myNumber) == shadedHexes.end()) { - CStack *shere = LOCPLINT->cb->battleGetStackByPos(myNumber); - CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack); + const CStack *shere = LOCPLINT->cb->battleGetStackByPos(myNumber); + const CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack); if(shere) { if(shere->owner == LOCPLINT->playerID) //our stack @@ -762,7 +762,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) } else //available tile { - CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack); + const CStack *sactive = LOCPLINT->cb->battleGetStackByID(activeStack); if(LOCPLINT->cb->battleGetStackByID(activeStack)->creature->isFlying()) { CGI->curh->changeGraphic(1,2); @@ -805,7 +805,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) } else { - CStack * stackUnder = LOCPLINT->cb->battleGetStackByPos(myNumber); + const CStack * stackUnder = LOCPLINT->cb->battleGetStackByPos(myNumber); bool whichCase; //for cases 1, 2 and 3 switch(spellSelMode) { @@ -1031,7 +1031,7 @@ void CBattleInterface::stackMoved(int number, int destHex, bool endMoving, int d int steps = creAnims[number]->framesInGroup(0)*getAnimSpeedMultiplier()-1; int hexWbase = 44, hexHbase = 42; bool twoTiles = LOCPLINT->cb->battleGetCreature(number).isDoubleWide(); - CStack * movedStack = LOCPLINT->cb->battleGetStackByID(number); + const CStack * movedStack = LOCPLINT->cb->battleGetStackByID(number); std::pair begPosition = CBattleHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack->creature); std::pair endPosition = CBattleHex::getXYUnitAnim(destHex, movedStack->attackerOwned, movedStack->creature); @@ -1451,7 +1451,7 @@ bool CBattleInterface::isTileAttackable(const int & number) const void CBattleInterface::handleEndOfMove(int stackNumber, int destinationTile) { - CStack * movedStack = LOCPLINT->cb->battleGetStackByID(stackNumber); + const CStack * movedStack = LOCPLINT->cb->battleGetStackByID(stackNumber); if(creAnims[stackNumber]->framesInGroup(21)!=0) // some units don't have this animation (ie. halberdier) { if (movedStack->creature->sounds.endMoving) @@ -1514,7 +1514,7 @@ void CBattleInterface::hexLclicked(int whichOne) } else { - CStack* dest = LOCPLINT->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one + const CStack* dest = LOCPLINT->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one if(!dest || !dest->alive()) //no creature at that tile { if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range @@ -1811,8 +1811,14 @@ void CBattleInterface::spellCast(SpellCast * sc) case 17: //lightning bolt displayEffect(1, sc->tile); displayEffect(spell.mainEffectAnim, sc->tile); + break; case 35: //dispel - displayEffect(spell.mainEffectAnim, sc->tile); + case 37: //cure + for(std::set::const_iterator it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it) + { + displayEffect(spell.mainEffectAnim, LOCPLINT->cb->battleGetStackByID(*it)->position); + } + break; } //switch(sc->id) //support for resistance @@ -1861,7 +1867,8 @@ void CBattleInterface::castThisSpell(int spellID) break; } } - if(CGI->spellh->spells[spellID].attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert + if(CGI->spellh->spells[spellID].attributes.find("CREATURE_TARGET_1") != std::string::npos || + CGI->spellh->spells[spellID].attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert { if(castingHero && castingHero->getSpellSecLevel(spellID) < 3) { @@ -2030,7 +2037,7 @@ void CBattleInterface::attackingShowHelper() { attackingInfo->reversing = true; - CStack* aStackp = LOCPLINT->cb->battleGetStackByID(attackingInfo->ID); //attacking stack + const CStack* aStackp = LOCPLINT->cb->battleGetStackByID(attackingInfo->ID); //attacking stack if(aStackp == NULL) return; CStack aStack = *aStackp; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index d8ca7a23b..0c82ba2e5 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1180,7 +1180,7 @@ void CPlayerInterface::actionStarted(const BattleAction* action) battleInt->deactivate(); - CStack *stack = cb->battleGetStackByID(action->stackNumber); + const CStack *stack = cb->battleGetStackByID(action->stackNumber); char txt[400]; if(action->actionType == 1) @@ -1252,7 +1252,7 @@ BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn { boost::unique_lock un(*pim); - CStack *stack = cb->battleGetStackByID(stackID); + const CStack *stack = cb->battleGetStackByID(stackID); if(vstd::contains(stack->state,MOVED)) //this stack has moved and makes second action -> high morale { std::string hlp = CGI->generaltexth->allTexts[33]; @@ -1331,7 +1331,7 @@ void CPlayerInterface::battleAttack(BattleAttack *ba) assert(curAction); if(ba->lucky()) //lucky hit { - CStack *stack = cb->battleGetStackByID(ba->stackAttacking); + const CStack *stack = cb->battleGetStackByID(ba->stackAttacking); std::string hlp = CGI->generaltexth->allTexts[45]; boost::algorithm::replace_first(hlp,"%s",(stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str()); battleInt->console->addText(hlp); @@ -1346,7 +1346,7 @@ void CPlayerInterface::battleAttack(BattleAttack *ba) } else { - CStack * attacker = cb->battleGetStackByID(ba->stackAttacking); + const CStack * attacker = cb->battleGetStackByID(ba->stackAttacking); int shift = 0; if(ba->counter() && BattleInfo::mutualPosition(curAction->destinationTile, attacker->position) < 0) { diff --git a/config/spell_info.txt b/config/spell_info.txt index e198569e4..b4597bc73 100644 --- a/config/spell_info.txt +++ b/config/spell_info.txt @@ -37,7 +37,7 @@ 34 1 -1 0 0 0 X 35 0 41 0 0 0 X 36 1 -1 0 0 0 0 -37 1 -1 0 0 0 0 +37 1 39 0 0 0 0 38 1 -1 0 0 0 0 39 1 -1 0 0 0 0 40 1 -1 0 0 0 0 diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 4fcb84583..2c7d58841 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2098,7 +2098,8 @@ std::set BattleInfo::getAttackedCreatures(const CSpell * s, const CGHer } } } - else if(VLC->spellh->spells[s->id].attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert + else if(VLC->spellh->spells[s->id].attributes.find("CREATURE_TARGET_1") != std::string::npos + || VLC->spellh->spells[s->id].attributes.find("CREATURE_TARGET_2") != std::string::npos) //spell to be cast on a specific creature but massive on expert { if(caster->getSpellSchoolLevel(s) < 3) /*not expert */ { diff --git a/lib/NetPacks.h b/lib/NetPacks.h index abef89971..9fe133dcd 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -904,9 +904,10 @@ struct SpellCast : public CPackForClient//3009 ui8 skill; ui16 tile; //destination tile (may not be set in some global/mass spells std::vector resisted; //ids of creatures that resisted this spell + std::set affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure) template void serialize(Handler &h, const int version) { - h & side & id & skill & tile & resisted; + h & side & id & skill & tile & resisted & affectedCres; } }; @@ -950,6 +951,31 @@ struct BattleResultsApplied : public CPackForClient //3012 } }; +struct StacksHealedOrResurrected : public CPackForClient //3013 +{ + StacksHealedOrResurrected(){type = 3013;} + + DLL_EXPORT void applyGs(CGameState *gs); + + struct HealInfo + { + ui32 stackID; + ui32 healForFirstStack; + ui32 resurrectedCres; + template void serialize(Handler &h, const int version) + { + h & stackID & healForFirstStack & resurrectedCres; + } + }; + + std::vector healedStacks; + + template void serialize(Handler &h, const int version) + { + h & healedStacks; + } +}; + struct ShowInInfobox : public CPackForClient //107 { ShowInInfobox(){type = 107;}; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 3566984cd..a136cdeb9 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -696,18 +696,21 @@ DLL_EXPORT void SpellCast::applyGs( CGameState *gs ) if(gs->curB && id == 35) //dispel { - CStack *s = gs->curB->getStackT(tile); - if(s && !vstd::contains(resisted, s->ID)) //if stack exists and it didn't resist + for(std::set::const_iterator it = affectedCres.begin(); it != affectedCres.end(); ++it) { - s->effects.clear(); //removing all effects - //removing all features from spells - std::vector tmpFeatures = s->features; - s->features.clear(); - for(int i=0; i < tmpFeatures.size(); i++) + CStack *s = gs->curB->getStack(*it); + if(s && !vstd::contains(resisted, s->ID)) //if stack exists and it didn't resist { - if(tmpFeatures[i].source != StackFeature::SPELL_EFFECT) + s->effects.clear(); //removing all effects + //removing all features from spells + std::vector tmpFeatures = s->features; + s->features.clear(); + for(int i=0; i < tmpFeatures.size(); i++) { - s->features.push_back(tmpFeatures[i]); + if(tmpFeatures[i].source != StackFeature::SPELL_EFFECT) + { + s->features.push_back(tmpFeatures[i]); + } } } } @@ -797,6 +800,12 @@ static std::vector stackEffectToFeature(const CStack::StackEffect break; } + //setting positiveness + for(int g=0; gspellh->spells[sse.id].positiveness; + } + return sf; } @@ -867,6 +876,37 @@ DLL_EXPORT void StacksInjured::applyGs( CGameState *gs ) stackAttacked.applyGs(gs); } +DLL_EXPORT void StacksHealedOrResurrected::applyGs( CGameState *gs ) +{ + for(int g=0; gcurB->stacks[healedStacks[g].stackID]; + changedStack->firstHPleft += healedStacks[g].healForFirstStack; + changedStack->amount += healedStacks[g].resurrectedCres; + //removal of negative effects + { + for(int h=0; heffects.size(); ++h) + { + if(VLC->spellh->spells[changedStack->effects[h].id].positiveness < 0) + { + changedStack->effects.erase(changedStack->effects.begin() + h); + } + } + + //removing all features from negative spells + std::vector tmpFeatures = changedStack->features; + changedStack->features.clear(); + for(int i=0; i < tmpFeatures.size(); i++) + { + if(tmpFeatures[i].source != StackFeature::SPELL_EFFECT || tmpFeatures[i].positiveness >= 0) + { + changedStack->features.push_back(tmpFeatures[i]); + } + } + } + } +} + DLL_EXPORT void YourTurn::applyGs( CGameState *gs ) { gs->currentPlayer = player; diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index 046a9a417..a13157b03 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -97,6 +97,7 @@ void registerTypes2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/StackFeature.h b/lib/StackFeature.h index 966b6a1d0..c257ee367 100644 --- a/lib/StackFeature.h +++ b/lib/StackFeature.h @@ -109,6 +109,7 @@ struct StackFeature ui8 type;//ECombatFeatures ui8 duration;//EDuration ui8 source;//ESource + si8 positiveness; //+1 - positive, 0 - neutral, -1 - negative; used mostly for spell features ui16 turnsRemain; //if duration is N_TURNS it describes how long the effect will last si16 subtype; //subtype of bonus/feature si32 value; @@ -123,7 +124,7 @@ struct StackFeature template void serialize(Handler &h, const int version) { - h & type & duration & source & turnsRemain & subtype & value & additionalInfo; + h & type & duration & source & positiveness & turnsRemain & subtype & value & additionalInfo; } }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index c0ae5d63a..0b8ecad70 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2722,6 +2722,10 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) //calculating affected creatures for all spells std::set attackedCres = gs->curB->getAttackedCreatures(s, h, ba.destinationTile); + for(std::set::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) + { + sc.affectedCres.insert((*it)->ID); + } //checking if creatures resist sc.resisted = calculateResistedStacks(s, h, secondHero, attackedCres); @@ -2752,7 +2756,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) BattleStackAttacked bsa; bsa.flags |= 2; bsa.effect = VLC->spellh->spells[ba.additionalInfo].mainEffectAnim; - bsa.damageAmount = calculateSpellDmg(&VLC->spellh->spells[ba.additionalInfo], h, *it); + bsa.damageAmount = calculateSpellDmg(s, h, *it); bsa.stackAttacked = (*it)->ID; prepareAttacked(bsa,*it); si.stacks.insert(bsa); @@ -2814,6 +2818,24 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) sendAndApply(&sse); break; } + case 37: //cure + { + StacksHealedOrResurrected shr; + for(std::set::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) + { + if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell + continue; + StacksHealedOrResurrected::HealInfo hi; + hi.stackID = (*it)->ID; + int healedHP = h->getPrimSkillLevel(2) * 5 + s->powers[h->getSpellSchoolLevel(s)]; + hi.healForFirstStack = std::min(healedHP, (*it)->MaxHealth() - (*it)->firstHPleft); + hi.resurrectedCres = 0; + shr.healedStacks.push_back(hi); + } + if(!shr.healedStacks.empty()) + sendAndApply(&shr); + break; + } } sendAndApply(&EndAction()); return true;