diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index b9621fe2b..9f2413393 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -24,6 +24,7 @@ public: void battleStackMoved(const CStack * stack, std::vector dest, int distance) OVERRIDE; void battleSpellCast(const BattleSpellCast *sc) OVERRIDE; void battleStacksEffectsSet(const SetStackEffect & sse) OVERRIDE;//called when a specific effect is set to stacks + //void battleTriggerEffect(const BattleTriggerEffect & bte) OVERRIDE; void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE; //called by engine when battle starts; side=0 - left, side=1 - right void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) OVERRIDE; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp void battleNewStackAppeared(const CStack * stack) OVERRIDE; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned diff --git a/AUTHORS b/AUTHORS index 348308573..917daa0ca 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,7 +29,7 @@ Xiaomin Ding, * smack videos player Tom Zielinski aka Warmonger, - * support for some of map objects and other items + * game objects, mechanics Frank Zago aka ubuntux, <> * GCC/Linux compatibility changes, sound/music support, video support on Linux diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 8dca16c3c..aa86ee4f9 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -2631,47 +2631,6 @@ void CBattleInterface::stackRemoved(int stackID) void CBattleInterface::stackActivated(const CStack * stack) //TODO: check it all before game state is changed due to abilities { - //don't show animation when no HP is regenerated - if (stack->firstHPleft != stack->MaxHealth()) - { - if( stack->hasBonusOfType(Bonus::HP_REGENERATION) || stack->hasBonusOfType(Bonus::FULL_HP_REGENERATION, 1)) - { - displayEffect(74, stack->position); - CCS->soundh->playSound(soundBase::REGENER); - } - if( stack->hasBonusOfType(Bonus::FULL_HP_REGENERATION, 0)) - { - displayEffect(74, stack->position); - CCS->soundh->playSound(soundBase::REGENER); - } - } - - if(stack->hasBonusOfType(Bonus::MANA_DRAIN)) - { - CGHeroInstance * enemy = NULL; //probably could be smarter and not duplicated - if (defendingHero) - if (defendingHero->myHero->tempOwner != stack->owner) - enemy = const_cast(defendingHero->myHero); - if (attackingHero) - if (attackingHero->myHero->tempOwner != stack->owner) - enemy = const_cast(attackingHero->myHero); - if (enemy) - { - ui32 manaDrained = stack->valOfBonuses(Bonus::MANA_DRAIN); - amin (manaDrained, enemy->mana); - if (manaDrained) - { - displayEffect(77, stack->position); - CCS->soundh->playSound(soundBase::MANADRAI); - } - } - } - if(stack->hasBonusOfType(Bonus::POISON)) - { - displayEffect(67, stack->position); - CCS->soundh->playSound(soundBase::POISON); - } - //givenCommand = NULL; stackToActivate = stack; if(pendingAnims.size() == 0) @@ -3546,6 +3505,38 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile) addNewAnim(new CSpellEffectAnim(this, effect, destTile)); } +void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte) +{ + const CStack * stack = curInt->cb->battleGetStackByID(bte.stackID); + //don't show animation when no HP is regenerated + switch (bte.effect) + { + case Bonus::HP_REGENERATION: + if( stack->hasBonusOfType(Bonus::HP_REGENERATION) || stack->hasBonusOfType(Bonus::FULL_HP_REGENERATION, 1)) + { + displayEffect(74, stack->position); + CCS->soundh->playSound(soundBase::REGENER); + } + if( stack->hasBonusOfType(Bonus::FULL_HP_REGENERATION, 0)) + { + displayEffect(74, stack->position); + CCS->soundh->playSound(soundBase::REGENER); + } + break; + case Bonus::MANA_DRAIN: + displayEffect(77, stack->position); + CCS->soundh->playSound(soundBase::MANADRAI); + break; + case Bonus::POISON: + displayEffect(67, stack->position); + CCS->soundh->playSound(soundBase::POISON); + break; + default: + return; + } + //waitForAnims(); //fixme: freezes game :? +} + void CBattleInterface::setAnimSpeed(int set) { curInt->sysOpts.animSpeed = set; diff --git a/client/CBattleInterface.h b/client/CBattleInterface.h index 7303cace6..72dc1069a 100644 --- a/client/CBattleInterface.h +++ b/client/CBattleInterface.h @@ -37,6 +37,7 @@ class CGTownInstance; struct CatapultAttack; class CBattleInterface; struct CatapultProjectileInfo; +struct BattleTriggerEffect; /// Small struct which contains information about the id of the attacked stack, the damage dealt,... struct SStackAttackedInfo @@ -569,6 +570,7 @@ public: void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks void castThisSpell(int spellID); //called when player has chosen a spell from spellbook void displayEffect(ui32 effect, int destTile); //displays effect of a spell on the battlefield; affected: true - attacker. false - defender + void battleTriggerEffect(const BattleTriggerEffect & bte); void setBattleCursor(const int myNumber); //really complex and messy void endAction(const BattleAction* action); void hideQueue(); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 48f5d8679..b3508c55f 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -801,6 +801,10 @@ void CPlayerInterface::battleStacksEffectsSet( const SetStackEffect & sse ) boost::unique_lock un(*pim); battleInt->battleStacksEffectsSet(sse); } +void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte) +{ + battleInt->battleTriggerEffect(bte); +} void CPlayerInterface::battleStacksAttacked(const std::vector & bsa) { if(LOCPLINT != this) diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index ac2e0ca0a..db4473bb6 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -224,6 +224,7 @@ public: void battleStackMoved(const CStack * stack, std::vector dest, int distance) OVERRIDE; void battleSpellCast(const BattleSpellCast *sc) OVERRIDE; void battleStacksEffectsSet(const SetStackEffect & sse) OVERRIDE; //called when a specific effect is set to stacks + void battleTriggerEffect(const BattleTriggerEffect & bte) OVERRIDE; //various one-shot effect void battleStacksAttacked(const std::vector & bsa) OVERRIDE; void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) OVERRIDE; //called by engine when battle starts; side=0 - left, side=1 - right void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) OVERRIDE; //called when stacks are healed / resurrected diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index dc6edfc46..401d7b70b 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -587,6 +587,11 @@ void BattleSetActiveStack::applyCl( CClient *cl ) boost::thread( boost::bind(&CClient::waitForMoveAndSend, cl, playerToCall) ); } +void BattleTriggerEffect::applyCl(CClient * cl) +{ + BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleTriggerEffect, *this); +} + void BattleResult::applyFirstCl( CClient *cl ) { BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleEnd,this); diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 300030c4e..cb6f41239 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -126,6 +126,7 @@ public: virtual void battleNewRoundFirst(int round); virtual void actionFinished(const BattleAction *action); virtual void battleStacksEffectsSet(const SetStackEffect & sse); + //virtual void battleTriggerEffect(const BattleTriggerEffect & bte); virtual void battleStacksRemoved(const BattleStacksRemoved & bsr); virtual void battleObstaclesRemoved(const std::set & removedObstacles); virtual void battleNewStackAppeared(const CStack * stack); diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 36f2fff2a..7e1acece9 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -30,6 +30,7 @@ class CStack; class CCreatureSet; struct BattleAttack; struct SetStackEffect; +struct BattleTriggerEffect; class DLL_EXPORT IBattleEventsReceiver @@ -45,6 +46,7 @@ public: virtual void battleStackMoved(const CStack * stack, std::vector dest, int distance){}; virtual void battleSpellCast(const BattleSpellCast *sc){}; virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks + virtual void battleTriggerEffect(const BattleTriggerEffect & bte){}; //called for various one-shot effects virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp virtual void battleNewStackAppeared(const CStack * stack){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned diff --git a/lib/NetPacks.h b/lib/NetPacks.h index a426251af..9501cc645 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1522,6 +1522,26 @@ struct BattleSetStackProperty : public CPackForClient //3018 } }; +struct BattleTriggerEffect : public CPackForClient //3019 +{ //activated at the beginning of turn + BattleTriggerEffect(){type = 3019;}; + + DLL_EXPORT void applyGs(CGameState *gs); //effect + void applyCl(CClient *cl); //play animations & stuff + + //enum BattleEffect {REGENERATION, MANA_DRAIN, FEAR, MANA_CHANNELING, ENCHANTER, UNBIND, POISON, ENCHANTED, SUMMONER}; + + int stackID; + int effect; //use enumBattleEffect or corresponding Bonus type for sanity? + int val; + int additionalInfo; + + template void serialize(Handler &h, const int version) + { + h & stackID & effect & val & additionalInfo; + } +}; + struct ShowInInfobox : public CPackForClient //107 { ShowInInfobox(){type = 107;}; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index e694bc997..238687d16 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -858,34 +858,6 @@ DLL_EXPORT void BattleSetActiveStack::applyGs( CGameState *gs ) gs->curB->activeStack = stack; CStack *st = gs->curB->getStack(stack); - if (st->alive()) - { - //regeneration - if(st->hasBonusOfType(Bonus::HP_REGENERATION)) - st->firstHPleft = std::min( st->MaxHealth(), st->valOfBonuses(Bonus::HP_REGENERATION) ); - if(st->hasBonusOfType(Bonus::FULL_HP_REGENERATION)) - st->firstHPleft = st->MaxHealth(); - if(st->hasBonusOfType(Bonus::POISON)) - { - Bonus * b = st->getBonus(Selector::source(Bonus::SPELL_EFFECT, 71) && Selector::type(Bonus::STACK_HEALTH)); - if (b) //TODO: what if not?... - { - b->val -= 10; - amax (b->val, -(st->valOfBonuses(Bonus::POISON))); - } - } - if(st->hasBonusOfType(Bonus::MANA_DRAIN)) - { - const CGHeroInstance * enemy = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner)); - if (enemy) - { - ui32 manaDrained = st->valOfBonuses(Bonus::MANA_DRAIN); - amin (manaDrained, gs->curB->heroes[0]->mana); - gs->getHero(enemy->id)->mana -= manaDrained; //jeez, it's overcomplicate - } - } - } - //remove bonuses that last until when stack gets new turn st->getBonusList().remove_if(Bonus::UntilGetsTurn); @@ -893,6 +865,36 @@ DLL_EXPORT void BattleSetActiveStack::applyGs( CGameState *gs ) st->state.insert(HAD_MORALE); } +DLL_EXPORT void BattleTriggerEffect::applyGs( CGameState *gs ) +{ + CStack *st = gs->curB->getStack(stackID); + switch (effect) + { + case Bonus::HP_REGENERATION: + st->firstHPleft += val; + amin (st->firstHPleft, (ui32)st->MaxHealth()); + break; + case Bonus::MANA_DRAIN: + { + CGHeroInstance * h = gs->getHero(additionalInfo); + h->mana -= val; + amax(h->mana, 0); + break; + } + case Bonus::POISON: + { + Bonus * b = st->getBonus(Selector::source(Bonus::SPELL_EFFECT, 71) && Selector::type(Bonus::STACK_HEALTH)); + if (b) + b->val = val; + break; + } + case Bonus::ENCHANTER: + case Bonus::FEAR: + default: + tlog2 << "Unrecognized trigger effect type "<< type <<"\n"; + } +} + void BattleResult::applyGs( CGameState *gs ) { //stack with SUMMONED flag but coming from garrison -> most likely resurrected, needs to be removed diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index 1a55a47f4..d10b3be2f 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -150,6 +150,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/server/CGameHandler.cpp b/server/CGameHandler.cpp index 1099945eb..199f7f457 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3935,6 +3935,70 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) return false; } +void CGameHandler::stackTurnTrigger(const CStack * st) +{ + BattleTriggerEffect bte; + bte.stackID = st->ID; + bte.effect = -1; + bte.val = 0; + bte.additionalInfo = 0; + if (st->alive()) + { + //regeneration + if(st->hasBonusOfType(Bonus::HP_REGENERATION)) + { + bte.effect = Bonus::HP_REGENERATION; + bte.val = std::min((int)(st->MaxHealth() - st->firstHPleft), st->valOfBonuses(Bonus::HP_REGENERATION)); + } + if(st->hasBonusOfType(Bonus::FULL_HP_REGENERATION)) + { + bte.effect = Bonus::HP_REGENERATION; + bte.val = st->MaxHealth() - st->firstHPleft; + } + if (bte.val) //anything to heal + sendAndApply(&bte); + + if(st->hasBonusOfType(Bonus::POISON)) + { + const Bonus * b = st->getBonus(Selector::source(Bonus::SPELL_EFFECT, 71) && Selector::type(Bonus::STACK_HEALTH)); + if (b) //TODO: what if not?... + { + bte.val = std::max (b->val - 10, -(st->valOfBonuses(Bonus::POISON))); + if (bte.val < b->val) //(negative) poison effect increases - update it + { + bte.effect = Bonus::POISON; + sendAndApply(&bte); + } + } + } + if(st->hasBonusOfType(Bonus::MANA_DRAIN)) + { + const CGHeroInstance * enemy = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner)); + if (enemy) + { + ui32 manaDrained = st->valOfBonuses(Bonus::MANA_DRAIN); + amin (manaDrained, gs->curB->heroes[0]->mana); + if (manaDrained) + { + bte.effect = Bonus::MANA_DRAIN; + bte.val = manaDrained; + bte.additionalInfo = enemy->id; //for sanity + sendAndApply(&bte); + } + } + } + BonusList * bl = st->getBonuses(Selector::type(Bonus::ENCHANTER)).get(); + if (bl->size()) + { + bte.effect = Bonus::ENCHANTER; + int index = rand() % bl->size(); + bte.val = (*bl)[index]->subtype; //spell ID + bte.additionalInfo = (*bl)[index]->val; //spell level + sendAndApply(&bte); + } + } +} + void CGameHandler::handleTimeEvents() { gs->map->events.sort(evntCmp); @@ -5207,6 +5271,8 @@ void CGameHandler::runBattle() {//ask interface and wait for answer if(!battleResult.get()) { + stackTurnTrigger(next); //various effects + BattleSetActiveStack sas; sas.stack = next->ID; sendAndApply(&sas); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 7c4d52089..0ed269248 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -199,6 +199,7 @@ public: bool makeBattleAction(BattleAction &ba); void handleSpellCasting(int spellID, int spellLvl, THex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, SpellCasting::ECastingMode mode, const CStack * stack); bool makeCustomAction(BattleAction &ba); + void stackTurnTrigger(const CStack * stack); bool queryReply( ui32 qid, ui32 answer, ui8 player ); bool hireHero( const CGObjectInstance *obj, ui8 hid, ui8 player ); bool buildBoat( ui32 objid );