diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 7e3926a44..d247998df 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -980,17 +980,17 @@ CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBas { } -CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector pos, int effects): +CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector hex, int effects): CPointEffectAnimation(_owner, sound, animationName, effects) { - battlehexes = pos; + battlehexes = hex; } -CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, BattleHex pos, int effects): +CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, BattleHex hex, int effects): CPointEffectAnimation(_owner, sound, animationName, effects) { - assert(pos.isValid()); - battlehexes.push_back(pos); + assert(hex.isValid()); + battlehexes.push_back(hex); } CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector pos, int effects): @@ -1005,6 +1005,14 @@ CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBas positions.push_back(pos); } +CPointEffectAnimation::CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, Point pos, BattleHex hex, int effects): + CPointEffectAnimation(_owner, sound, animationName, effects) +{ + assert(hex.isValid()); + battlehexes.push_back(hex); + positions.push_back(pos); +} + bool CPointEffectAnimation::init() { if(!CBattleAnimation::checkInitialConditions()) @@ -1019,9 +1027,8 @@ bool CPointEffectAnimation::init() return false; } - if (positions.empty() && battlehexes.empty()) + if (screenFill()) { - //armageddon, create screen fill for(int i=0; i * first->width() < owner->pos.w ; ++i) for(int j=0; j * first->height() < owner->pos.h ; ++j) positions.push_back(Point(i * first->width(), j * first->height())); @@ -1032,35 +1039,36 @@ bool CPointEffectAnimation::init() be.animation = animation; be.currentFrame = 0; - for ( auto const position : positions) + for (size_t i = 0; i < std::max(battlehexes.size(), positions.size()); ++i) { - be.x = position.x; - be.y = position.y; - be.position = BattleHex::INVALID; + bool hasTile = i < battlehexes.size(); + bool hasPosition = i < positions.size(); - owner->effectsController->battleEffects.push_back(be); - } - - for ( auto const tile : battlehexes) - { - const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(tile, false); - - assert(tile.isValid()); - if(!tile.isValid()) - continue; - - Rect tilePos = owner->fieldController->hexPosition(tile); - be.position = tile; - be.x = tilePos.x + tilePos.w/2 - first->width()/2; - - if(destStack && destStack->doubleWide()) // Correction for 2-hex creatures. - be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2; - - if (alignToBottom()) - be.y = tilePos.y + tilePos.h - first->height(); + if (hasTile && !forceOnTop()) + be.position = battlehexes[i]; else - be.y = tilePos.y - first->height()/2; + be.position = BattleHex::INVALID; + if (hasPosition) + { + be.x = positions[i].x; + be.y = positions[i].y; + } + else + { + const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(battlehexes[i], false); + Rect tilePos = owner->fieldController->hexPosition(battlehexes[i]); + + be.x = tilePos.x + tilePos.w/2 - first->width()/2; + + if(destStack && destStack->doubleWide()) // Correction for 2-hex creatures. + be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2; + + if (alignToBottom()) + be.y = tilePos.y + tilePos.h - first->height(); + else + be.y = tilePos.y - first->height()/2; + } owner->effectsController->battleEffects.push_back(be); } return true; @@ -1072,7 +1080,11 @@ void CPointEffectAnimation::nextFrame() playEffect(); if (soundFinished && effectFinished) + { + //remove visual effect itself only if sound has finished as well - necessary for obstacles like force field + clearEffect(); delete this; + } } bool CPointEffectAnimation::alignToBottom() const @@ -1085,10 +1097,19 @@ bool CPointEffectAnimation::waitForSound() const return effectFlags & WAIT_FOR_SOUND; } +bool CPointEffectAnimation::forceOnTop() const +{ + return effectFlags & FORCE_ON_TOP; +} + +bool CPointEffectAnimation::screenFill() const +{ + return effectFlags & SCREEN_FILL; +} + void CPointEffectAnimation::onEffectFinished() { effectFinished = true; - clearEffect(); } void CPointEffectAnimation::onSoundFinished() @@ -1118,6 +1139,9 @@ void CPointEffectAnimation::playSound() void CPointEffectAnimation::playEffect() { + if ( effectFinished ) + return; + for(auto & elem : owner->effectsController->battleEffects) { if(elem.effectID == ID) @@ -1126,6 +1150,7 @@ void CPointEffectAnimation::playEffect() if(elem.currentFrame >= elem.animation->size()) { + elem.currentFrame = elem.animation->size() - 1; onEffectFinished(); break; } diff --git a/client/battle/CBattleAnimations.h b/client/battle/CBattleAnimations.h index 25a3f8c0f..f1fed4155 100644 --- a/client/battle/CBattleAnimations.h +++ b/client/battle/CBattleAnimations.h @@ -262,6 +262,19 @@ public: CCastAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell); }; +struct CPointEffectParameters +{ + std::vector positions; + std::vector tiles; + std::string animation; + + soundBase::soundID sound = soundBase::invalid; + BattleHex boundHex = BattleHex::INVALID; + bool aligntoBottom = false; + bool waitForSound = false; + bool screenFill = false; +}; + /// Class that plays effect at one or more positions along with (single) sound effect class CPointEffectAnimation : public CBattleAnimation { @@ -277,6 +290,8 @@ class CPointEffectAnimation : public CBattleAnimation bool alignToBottom() const; bool waitForSound() const; + bool forceOnTop() const; + bool screenFill() const; void onEffectFinished(); void onSoundFinished(); @@ -289,7 +304,9 @@ public: enum EEffectFlags { ALIGN_TO_BOTTOM = 1, - WAIT_FOR_SOUND = 2 + WAIT_FOR_SOUND = 2, + FORCE_ON_TOP = 4, + SCREEN_FILL = 8, }; /// Create animation with screen-wide effect @@ -300,8 +317,10 @@ public: CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector pos , int effects = 0); /// Create animation positioned at certain hex(es) - CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, BattleHex pos , int effects = 0); - CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector pos, int effects = 0); + CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, BattleHex hex , int effects = 0); + CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, std::vector hex, int effects = 0); + + CPointEffectAnimation(CBattleInterface * _owner, soundBase::soundID sound, std::string animationName, Point pos, BattleHex hex, int effects = 0); ~CPointEffectAnimation(); bool init() override; diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 4274cbc10..8bf36f61e 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -583,7 +583,7 @@ void CBattleInterface::displayBattleLog(const std::vector & battleLo } } -void CBattleInterface::displaySpellAnimationQueue(const CSpell::TAnimationQueue & q, BattleHex destinationTile) +void CBattleInterface::displaySpellAnimationQueue(const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit) { for(const CSpell::TAnimation & animation : q) { @@ -591,12 +591,21 @@ void CBattleInterface::displaySpellAnimationQueue(const CSpell::TAnimationQueue stacksController->addNewAnim(new CDummyAnimation(this, animation.pause)); else { + int flags = 0; + + if (isHit) + flags |= CPointEffectAnimation::FORCE_ON_TOP; + + if (animation.verticalPosition == VerticalPosition::BOTTOM) + flags |= CPointEffectAnimation::ALIGN_TO_BOTTOM; + if (!destinationTile.isValid()) - stacksController->addNewAnim(new CPointEffectAnimation(this, soundBase::invalid, animation.resourceName)); - else if (animation.verticalPosition == VerticalPosition::BOTTOM) - stacksController->addNewAnim(new CPointEffectAnimation(this, soundBase::invalid, animation.resourceName, destinationTile, CPointEffectAnimation::ALIGN_TO_BOTTOM)); + flags |= CPointEffectAnimation::SCREEN_FILL; + + if (!destinationTile.isValid()) + stacksController->addNewAnim(new CPointEffectAnimation(this, soundBase::invalid, animation.resourceName, flags)); else - stacksController->addNewAnim(new CPointEffectAnimation(this, soundBase::invalid, animation.resourceName, destinationTile)); + stacksController->addNewAnim(new CPointEffectAnimation(this, soundBase::invalid, animation.resourceName, destinationTile, flags)); } } } @@ -606,7 +615,7 @@ void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTi const CSpell * spell = spellID.toSpell(); if(spell) - displaySpellAnimationQueue(spell->animationInfo.cast, destinationTile); + displaySpellAnimationQueue(spell->animationInfo.cast, destinationTile, false); } void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile) @@ -614,7 +623,7 @@ void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destination const CSpell *spell = spellID.toSpell(); if(spell) - displaySpellAnimationQueue(spell->animationInfo.affect, destinationTile); + displaySpellAnimationQueue(spell->animationInfo.affect, destinationTile, false); } void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile) @@ -622,7 +631,7 @@ void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTil const CSpell * spell = spellID.toSpell(); if(spell) - displaySpellAnimationQueue(spell->animationInfo.hit, destinationTile); + displaySpellAnimationQueue(spell->animationInfo.hit, destinationTile, true); } void CBattleInterface::setAnimSpeed(int set) diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index cbddabe08..420f83221 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -166,7 +166,7 @@ public: void displayBattleLog(const std::vector & battleLog); - void displaySpellAnimationQueue(const CSpell::TAnimationQueue & q, BattleHex destinationTile); + void displaySpellAnimationQueue(const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit); void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation void displaySpellHit(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation diff --git a/client/battle/CBattleObstacleController.cpp b/client/battle/CBattleObstacleController.cpp index 9243c3a1f..79dadfc87 100644 --- a/client/battle/CBattleObstacleController.cpp +++ b/client/battle/CBattleObstacleController.cpp @@ -85,15 +85,7 @@ void CBattleObstacleController::obstaclePlaced(const std::vectorappearAnimation; - - //TODO: sound - //soundBase::QUIKSAND - //soundBase::LANDMINE - //soundBase::FORCEFLD - //soundBase::fireWall - - auto animation = std::make_shared(defname); + auto animation = std::make_shared(spellObstacle->appearAnimation); animation->preload(); auto first = animation->getImage(0, 0); @@ -103,10 +95,14 @@ void CBattleObstacleController::obstaclePlaced(const std::vector if we know how to blit obstacle, let's blit the effect in the same place Point whereTo = getObstaclePosition(first, *oi); - owner->stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::QUIKSAND, defname, whereTo, CPointEffectAnimation::WAIT_FOR_SOUND)); + owner->stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::invalid, spellObstacle->appearAnimation, whereTo, oi->pos, CPointEffectAnimation::WAIT_FOR_SOUND)); //so when multiple obstacles are added, they show up one after another owner->waitForAnims(); diff --git a/client/battle/CBattleSiegeController.cpp b/client/battle/CBattleSiegeController.cpp index d3807a256..1733ab140 100644 --- a/client/battle/CBattleSiegeController.cpp +++ b/client/battle/CBattleSiegeController.cpp @@ -333,7 +333,7 @@ void CBattleSiegeController::stackIsCatapulting(const CatapultAttack & ca) positions.push_back(owner->stacksController->getStackPositionAtHex(attackInfo.destinationTile, nullptr) + Point(99, 120)); - owner->stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::invalid, "SGEXPL.DEF", positions)); + owner->stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::WALLHIT, "SGEXPL.DEF", positions)); } owner->waitForAnims();