Selaa lähdekoodia

moved battle obstacle handling into separate class

Ivan Savenko 2 vuotta sitten
vanhempi
sitoutus
9c2a6dc9fc

+ 10 - 187
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<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())
 	{

+ 3 - 9
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<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;
 };

+ 203 - 0
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<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);
+		}
+	}
+}

+ 26 - 0
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<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();
 };

+ 3 - 1
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();