From 9c2a6dc9fc4ca5fcb99a15e72b278f477f7b1ae2 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Nov 2022 19:36:25 +0200 Subject: [PATCH] moved battle obstacle handling into separate class --- client/battle/CBattleInterface.cpp | 197 +------------------ client/battle/CBattleInterface.h | 12 +- client/battle/CBattleObstacleController.cpp | 203 ++++++++++++++++++++ client/battle/CBattleObstacleController.h | 26 +++ client/battle/CBattleSiegeController.cpp | 4 +- 5 files changed, 245 insertions(+), 197 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 41fda409d..7b39db2b8 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -14,6 +14,7 @@ #include "CBattleInterfaceClasses.h" #include "CCreatureAnimation.h" #include "CBattleProjectileController.h" +#include "CBattleObstacleController.h" #include "CBattleSiegeController.h" #include "../CBitmapHandler.h" @@ -335,48 +336,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet backgroundWithHexes = CSDL_Ext::newSurface(background->w, background->h, screen); - //preparing obstacle defs - auto obst = curInt->cb->battleGetAllObstacles(); - for(auto & elem : obst) - { - if(elem->obstacleType == CObstacleInstance::USUAL) - { - std::string animationName = elem->getInfo().animation; - - auto cached = animationsCache.find(animationName); - - if(cached == animationsCache.end()) - { - auto animation = std::make_shared(animationName); - animationsCache[animationName] = animation; - obstacleAnimations[elem->uniqueID] = animation; - animation->preload(); - } - else - { - obstacleAnimations[elem->uniqueID] = cached->second; - } - } - else if (elem->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) - { - std::string animationName = elem->getInfo().animation; - - auto cached = animationsCache.find(animationName); - - if(cached == animationsCache.end()) - { - auto animation = std::make_shared(); - animation->setCustom(animationName, 0, 0); - animationsCache[animationName] = animation; - obstacleAnimations[elem->uniqueID] = animation; - animation->preload(); - } - else - { - obstacleAnimations[elem->uniqueID] = cached->second; - } - } - } + obstacleController.reset(new CBattleObstacleController(this)); for(auto hex : bfield) addChild(hex.get()); @@ -2625,45 +2585,7 @@ Rect CBattleInterface::hexPosition(BattleHex hex) const void CBattleInterface::obstaclePlaced(const CObstacleInstance & oi) { - //so when multiple obstacles are added, they show up one after another - waitForAnims(); - - //soundBase::soundID sound; // FIXME(v.markovtsev): soundh->playSound() is commented in the end => warning - - std::string defname; - - switch(oi.obstacleType) - { - case CObstacleInstance::SPELL_CREATED: - { - auto &spellObstacle = dynamic_cast(oi); - defname = spellObstacle.appearAnimation; - //TODO: sound - //soundBase::QUIKSAND - //soundBase::LANDMINE - //soundBase::FORCEFLD - //soundBase::fireWall - } - break; - default: - logGlobal->error("I don't know how to animate appearing obstacle of type %d", (int)oi.obstacleType); - return; - } - - auto animation = std::make_shared(defname); - animation->preload(); - - auto first = animation->getImage(0, 0); - if(!first) - return; - - //we assume here that effect graphics have the same size as the usual obstacle image - // -> if we know how to blit obstacle, let's blit the effect in the same place - Point whereTo = getObstaclePosition(first, oi); - addNewAnim(new CEffectAnimation(this, animation, whereTo.x, whereTo.y)); - - //TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad - //CCS->soundh->playSound(sound); + obstacleController->obstaclePlaced(oi); } const CGHeroInstance *CBattleInterface::currentHero() const @@ -2772,7 +2694,9 @@ void CBattleInterface::showBackground(SDL_Surface *to) else { showBackgroundImage(to); - showAbsoluteObstacles(to); + obstacleController->showAbsoluteObstacles(to); + if ( siegeController ) + siegeController->showAbsoluteObstacles(to); } showHighlightedHexes(to); } @@ -2786,22 +2710,6 @@ void CBattleInterface::showBackgroundImage(SDL_Surface *to) } } -void CBattleInterface::showAbsoluteObstacles(SDL_Surface * to) -{ - //Blit absolute obstacles - for(auto & oi : curInt->cb->battleGetAllObstacles()) - { - if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) - { - auto img = getObstacleImage(*oi); - if(img) - img->draw(to, pos.x + oi->getInfo().width, pos.y + oi->getInfo().height); - } - } - - if ( siegeController ) - siegeController->showAbsoluteObstacles(to); -} void CBattleInterface::showHighlightedHexes(SDL_Surface *to) { @@ -2902,7 +2810,7 @@ void CBattleInterface::showBattlefieldObjects(SDL_Surface *to) { if (siegeController) siegeController->showPiecesOfWall(to, hex.walls); - showObstacles(to, hex.obstacles); + obstacleController->showObstacles(to, hex.obstacles); showAliveStacks(to, hex.alive); showBattleEffects(to, hex.effects); }; @@ -3039,19 +2947,6 @@ void CBattleInterface::showStacks(SDL_Surface *to, std::vector s } } -void CBattleInterface::showObstacles(SDL_Surface * to, std::vector> & obstacles) -{ - for(auto & obstacle : obstacles) - { - auto img = getObstacleImage(*obstacle); - if(img) - { - Point p = getObstaclePosition(img, *obstacle); - img->draw(to, p.x, p.y); - } - } -} - void CBattleInterface::showBattleEffects(SDL_Surface *to, const std::vector &battleEffects) { for (auto & elem : battleEffects) @@ -3180,19 +3075,8 @@ BattleObjectsByHex CBattleInterface::sortObjectsByHex() } // Sort obstacles - { - std::map> backgroundObstacles; - for (auto &obstacle : curInt->cb->battleGetAllObstacles()) { - if (obstacle->obstacleType != CObstacleInstance::ABSOLUTE_OBSTACLE - && obstacle->obstacleType != CObstacleInstance::MOAT) { - backgroundObstacles[obstacle->pos] = obstacle; - } - } - for (auto &op : backgroundObstacles) - { - sorted.beforeAll.obstacles.push_back(op.second); - } - } + obstacleController->sortObjectsByHex(sorted); + // Sort wall parts if (siegeController) siegeController->sortObjectsByHex(sorted); @@ -3237,58 +3121,6 @@ void CBattleInterface::updateBattleAnimations() } } -std::shared_ptr CBattleInterface::getObstacleImage(const CObstacleInstance & oi) -{ - int frameIndex = (animCount+1) *25 / getAnimSpeed(); - std::shared_ptr animation; - - if(oi.obstacleType == CObstacleInstance::USUAL || oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) - { - animation = obstacleAnimations[oi.uniqueID]; - } - else if(oi.obstacleType == CObstacleInstance::SPELL_CREATED) - { - const SpellCreatedObstacle * spellObstacle = dynamic_cast(&oi); - if(!spellObstacle) - return std::shared_ptr(); - - std::string animationName = spellObstacle->animation; - - auto cacheIter = animationsCache.find(animationName); - - if(cacheIter == animationsCache.end()) - { - logAnim->trace("Creating obstacle animation %s", animationName); - - animation = std::make_shared(animationName); - animation->preload(); - animationsCache[animationName] = animation; - } - else - { - animation = cacheIter->second; - } - } - - if(animation) - { - frameIndex %= animation->size(0); - return animation->getImage(frameIndex, 0); - } - - return nullptr; -} - -Point CBattleInterface::getObstaclePosition(std::shared_ptr image, const CObstacleInstance & obstacle) -{ - int offset = obstacle.getAnimationYOffset(image->height()); - - Rect r = hexPosition(obstacle.pos); - r.y += 42 - image->height() + offset; - - return r.topLeft(); -} - void CBattleInterface::redrawBackgroundWithHexes(const CStack *activeStack) { attackableHexes.clear(); @@ -3308,16 +3140,7 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack *activeStack) //prepare background graphic with hexes and shaded hexes blitAt(background, 0, 0, backgroundWithHexes); - //draw absolute obstacles (cliffs and so on) - for(auto & oi : curInt->cb->battleGetAllObstacles()) - { - if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) - { - auto img = getObstacleImage(*oi); - if(img) - img->draw(backgroundWithHexes, oi->getInfo().width, oi->getInfo().height); - } - } + obstacleController->redrawBackgroundWithHexes(); if (settings["battle"]["stackRange"].Bool()) { diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index b673c8e47..9d42af8a9 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -60,6 +60,7 @@ class IImage; class CBattleProjectileController; class CBattleSiegeController; +class CBattleObstacleController; /// Small struct which contains information about the id of the attacked stack, the damage dealt,... struct StackAttackedInfo @@ -139,9 +140,6 @@ private: const CGHeroInstance *attackingHeroInstance, *defendingHeroInstance; std::map> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID) - std::map> animationsCache; - std::map> obstacleAnimations; - std::map creDir; // //TODO: move it to battle callback ui8 animCount; const CStack *activeStack; //number of active stack; nullptr - no one @@ -200,7 +198,6 @@ private: void showBackground(SDL_Surface *to); void showBackgroundImage(SDL_Surface *to); - void showAbsoluteObstacles(SDL_Surface *to); void showHighlightedHexes(SDL_Surface *to); void showHighlightedHex(SDL_Surface *to, BattleHex hex, bool darkBorder = false); void showInterface(SDL_Surface *to); @@ -209,17 +206,12 @@ private: void showAliveStacks(SDL_Surface *to, std::vector stacks); void showStacks(SDL_Surface *to, std::vector stacks); - void showObstacles(SDL_Surface *to, std::vector> &obstacles); void showBattleEffects(SDL_Surface *to, const std::vector &battleEffects); BattleObjectsByHex sortObjectsByHex(); void updateBattleAnimations(); - std::shared_ptr getObstacleImage(const CObstacleInstance & oi); - - Point getObstaclePosition(std::shared_ptr image, const CObstacleInstance & obstacle); - void redrawBackgroundWithHexes(const CStack *activeStack); /** End of battle screen blitting methods */ @@ -227,6 +219,7 @@ private: public: std::unique_ptr projectilesController; std::unique_ptr siegeController; + std::unique_ptr obstacleController; static CondSh animsAreDisplayed; //for waiting with the end of battle for end of anims static CondSh givenCommand; //data != nullptr if we have i.e. moved current unit @@ -356,4 +349,5 @@ public: friend class CClickableHex; friend class CBattleProjectileController; friend class CBattleSiegeController; + friend class CBattleObstacleController; }; diff --git a/client/battle/CBattleObstacleController.cpp b/client/battle/CBattleObstacleController.cpp index b836f2ee4..7967ba4f4 100644 --- a/client/battle/CBattleObstacleController.cpp +++ b/client/battle/CBattleObstacleController.cpp @@ -9,4 +9,207 @@ */ #include "StdInc.h" #include "CBattleObstacleController.h" +#include "CBattleInterface.h" +#include "../CPlayerInterface.h" +#include "../../CCallback.h" +#include "../../lib/battle/CObstacleInstance.h" +#include "../../lib/ObstacleHandler.h" +#include "../gui/CAnimation.h" +CBattleObstacleController::CBattleObstacleController(CBattleInterface * owner): + owner(owner) +{ + auto obst = owner->curInt->cb->battleGetAllObstacles(); + for(auto & elem : obst) + { + if(elem->obstacleType == CObstacleInstance::USUAL) + { + std::string animationName = elem->getInfo().animation; + + auto cached = animationsCache.find(animationName); + + if(cached == animationsCache.end()) + { + auto animation = std::make_shared(animationName); + animationsCache[animationName] = animation; + obstacleAnimations[elem->uniqueID] = animation; + animation->preload(); + } + else + { + obstacleAnimations[elem->uniqueID] = cached->second; + } + } + else if (elem->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) + { + std::string animationName = elem->getInfo().animation; + + auto cached = animationsCache.find(animationName); + + if(cached == animationsCache.end()) + { + auto animation = std::make_shared(); + animation->setCustom(animationName, 0, 0); + animationsCache[animationName] = animation; + obstacleAnimations[elem->uniqueID] = animation; + animation->preload(); + } + else + { + obstacleAnimations[elem->uniqueID] = cached->second; + } + } + } +} + +void CBattleObstacleController::obstaclePlaced(const CObstacleInstance & oi) +{ + //so when multiple obstacles are added, they show up one after another + owner->waitForAnims(); + + //soundBase::soundID sound; // FIXME(v.markovtsev): soundh->playSound() is commented in the end => warning + + std::string defname; + + switch(oi.obstacleType) + { + case CObstacleInstance::SPELL_CREATED: + { + auto &spellObstacle = dynamic_cast(oi); + defname = spellObstacle.appearAnimation; + //TODO: sound + //soundBase::QUIKSAND + //soundBase::LANDMINE + //soundBase::FORCEFLD + //soundBase::fireWall + } + break; + default: + logGlobal->error("I don't know how to animate appearing obstacle of type %d", (int)oi.obstacleType); + return; + } + + auto animation = std::make_shared(defname); + animation->preload(); + + auto first = animation->getImage(0, 0); + if(!first) + return; + + //we assume here that effect graphics have the same size as the usual obstacle image + // -> if we know how to blit obstacle, let's blit the effect in the same place + Point whereTo = getObstaclePosition(first, oi); + owner->addNewAnim(new CEffectAnimation(owner, animation, whereTo.x, whereTo.y)); + + //TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad + //CCS->soundh->playSound(sound); +} + +void CBattleObstacleController::showAbsoluteObstacles(SDL_Surface * to) +{ + //Blit absolute obstacles + for(auto & oi : owner->curInt->cb->battleGetAllObstacles()) + { + if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) + { + auto img = getObstacleImage(*oi); + if(img) + img->draw(to, owner->pos.x + oi->getInfo().width, owner->pos.y + oi->getInfo().height); + } + } +} + +void CBattleObstacleController::showObstacles(SDL_Surface * to, std::vector> & obstacles) +{ + for(auto & obstacle : obstacles) + { + auto img = getObstacleImage(*obstacle); + if(img) + { + Point p = getObstaclePosition(img, *obstacle); + img->draw(to, p.x, p.y); + } + } +} + +void CBattleObstacleController::sortObjectsByHex(BattleObjectsByHex & sorted) +{ + std::map> backgroundObstacles; + for (auto &obstacle : owner->curInt->cb->battleGetAllObstacles()) { + if (obstacle->obstacleType != CObstacleInstance::ABSOLUTE_OBSTACLE + && obstacle->obstacleType != CObstacleInstance::MOAT) { + backgroundObstacles[obstacle->pos] = obstacle; + } + } + for (auto &op : backgroundObstacles) + { + sorted.beforeAll.obstacles.push_back(op.second); + } +} + + +std::shared_ptr CBattleObstacleController::getObstacleImage(const CObstacleInstance & oi) +{ + int frameIndex = (owner->animCount+1) *25 / owner->getAnimSpeed(); + std::shared_ptr animation; + + if(oi.obstacleType == CObstacleInstance::USUAL || oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) + { + animation = obstacleAnimations[oi.uniqueID]; + } + else if(oi.obstacleType == CObstacleInstance::SPELL_CREATED) + { + const SpellCreatedObstacle * spellObstacle = dynamic_cast(&oi); + if(!spellObstacle) + return std::shared_ptr(); + + std::string animationName = spellObstacle->animation; + + auto cacheIter = animationsCache.find(animationName); + + if(cacheIter == animationsCache.end()) + { + logAnim->trace("Creating obstacle animation %s", animationName); + + animation = std::make_shared(animationName); + animation->preload(); + animationsCache[animationName] = animation; + } + else + { + animation = cacheIter->second; + } + } + + if(animation) + { + frameIndex %= animation->size(0); + return animation->getImage(frameIndex, 0); + } + + return nullptr; +} + +Point CBattleObstacleController::getObstaclePosition(std::shared_ptr image, const CObstacleInstance & obstacle) +{ + int offset = obstacle.getAnimationYOffset(image->height()); + + Rect r = owner->hexPosition(obstacle.pos); + r.y += 42 - image->height() + offset; + + return r.topLeft(); +} + +void CBattleObstacleController::redrawBackgroundWithHexes() +{ + //draw absolute obstacles (cliffs and so on) + for(auto & oi : owner->curInt->cb->battleGetAllObstacles()) + { + if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) + { + auto img = getObstacleImage(*oi); + if(img) + img->draw(owner->backgroundWithHexes, oi->getInfo().width, oi->getInfo().height); + } + } +} diff --git a/client/battle/CBattleObstacleController.h b/client/battle/CBattleObstacleController.h index bf981ae35..e4b95fb10 100644 --- a/client/battle/CBattleObstacleController.h +++ b/client/battle/CBattleObstacleController.h @@ -9,7 +9,33 @@ */ #pragma once +struct SDL_Surface; +struct BattleObjectsByHex; +class IImage; +class CAnimation; +class CBattleInterface; +class CObstacleInstance; +struct Point; + class CBattleObstacleController { + std::map> animationsCache; + CBattleInterface * owner; + + std::map> obstacleAnimations; +public: + CBattleObstacleController(CBattleInterface * owner); + + void obstaclePlaced(const CObstacleInstance & oi); + void showObstacles(SDL_Surface *to, std::vector> &obstacles); + void showAbsoluteObstacles(SDL_Surface *to); + + void sortObjectsByHex(BattleObjectsByHex & sorted); + + std::shared_ptr getObstacleImage(const CObstacleInstance & oi); + + Point getObstaclePosition(std::shared_ptr image, const CObstacleInstance & obstacle); + + void redrawBackgroundWithHexes(); }; diff --git a/client/battle/CBattleSiegeController.cpp b/client/battle/CBattleSiegeController.cpp index f535a0673..6a1c545da 100644 --- a/client/battle/CBattleSiegeController.cpp +++ b/client/battle/CBattleSiegeController.cpp @@ -145,7 +145,9 @@ void CBattleSiegeController::printPartOfWall(SDL_Surface *to, int what) } -CBattleSiegeController::CBattleSiegeController(CBattleInterface * owner, const CGTownInstance *siegeTown) +CBattleSiegeController::CBattleSiegeController(CBattleInterface * owner, const CGTownInstance *siegeTown): + owner(owner), + town(siegeTown) { owner->background = BitmapHandler::loadBitmap( getSiegeName(0), false ); ui8 siegeLevel = owner->curInt->cb->battleGetSiegeLevel();