1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

moved battle obstacle handling into separate class

This commit is contained in:
Ivan Savenko 2022-11-17 19:36:25 +02:00
parent a65dd0726d
commit 9c2a6dc9fc
5 changed files with 245 additions and 197 deletions

View File

@ -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<CAnimation>(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<CAnimation>();
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<const SpellCreatedObstacle&>(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<CAnimation>(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<const CStack *> s
}
}
void CBattleInterface::showObstacles(SDL_Surface * to, std::vector<std::shared_ptr<const CObstacleInstance>> & 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<const BattleEffect *> &battleEffects)
{
for (auto & elem : battleEffects)
@ -3180,19 +3075,8 @@ BattleObjectsByHex CBattleInterface::sortObjectsByHex()
}
// Sort obstacles
{
std::map<BattleHex, std::shared_ptr<const CObstacleInstance>> 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<IImage> CBattleInterface::getObstacleImage(const CObstacleInstance & oi)
{
int frameIndex = (animCount+1) *25 / getAnimSpeed();
std::shared_ptr<CAnimation> 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<const SpellCreatedObstacle *>(&oi);
if(!spellObstacle)
return std::shared_ptr<IImage>();
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<CAnimation>(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<IImage> 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())
{

View File

@ -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<int32_t, std::shared_ptr<CCreatureAnimation>> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map<std::string, std::shared_ptr<CAnimation>> animationsCache;
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
std::map<int, bool> creDir; // <creatureID, if false reverse creature's animation> //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<const CStack *> stacks);
void showStacks(SDL_Surface *to, std::vector<const CStack *> stacks);
void showObstacles(SDL_Surface *to, std::vector<std::shared_ptr<const CObstacleInstance>> &obstacles);
void showBattleEffects(SDL_Surface *to, const std::vector<const BattleEffect *> &battleEffects);
BattleObjectsByHex sortObjectsByHex();
void updateBattleAnimations();
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
void redrawBackgroundWithHexes(const CStack *activeStack);
/** End of battle screen blitting methods */
@ -227,6 +219,7 @@ private:
public:
std::unique_ptr<CBattleProjectileController> projectilesController;
std::unique_ptr<CBattleSiegeController> siegeController;
std::unique_ptr<CBattleObstacleController> obstacleController;
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
static CondSh<BattleAction *> 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;
};

View File

@ -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<CAnimation>(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<CAnimation>();
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<const SpellCreatedObstacle&>(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<CAnimation>(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<std::shared_ptr<const CObstacleInstance>> & 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<BattleHex, std::shared_ptr<const CObstacleInstance>> 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<IImage> CBattleObstacleController::getObstacleImage(const CObstacleInstance & oi)
{
int frameIndex = (owner->animCount+1) *25 / owner->getAnimSpeed();
std::shared_ptr<CAnimation> 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<const SpellCreatedObstacle *>(&oi);
if(!spellObstacle)
return std::shared_ptr<IImage>();
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<CAnimation>(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<IImage> 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);
}
}
}

View File

@ -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<std::string, std::shared_ptr<CAnimation>> animationsCache;
CBattleInterface * owner;
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
public:
CBattleObstacleController(CBattleInterface * owner);
void obstaclePlaced(const CObstacleInstance & oi);
void showObstacles(SDL_Surface *to, std::vector<std::shared_ptr<const CObstacleInstance>> &obstacles);
void showAbsoluteObstacles(SDL_Surface *to);
void sortObjectsByHex(BattleObjectsByHex & sorted);
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
void redrawBackgroundWithHexes();
};

View File

@ -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();