mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-02 23:07:36 +02:00
BattleInt split is finished, start of refactoring:
- Refactoring of siege controller code - Replaced some usages of C struct SDL_Surface with proper c++ class IImage - Refactoring of rendering of battlefield objects (WIP)
This commit is contained in:
parent
cb6fe1eedf
commit
6b3beb05e5
@ -370,7 +370,7 @@ void CBattleActionsController::handleHex(BattleHex myNumber, int eventType)
|
||||
}
|
||||
break;
|
||||
case PossiblePlayerBattleAction::CATAPULT:
|
||||
if (owner->siegeController && owner->siegeController->isCatapultAttackable(myNumber))
|
||||
if (owner->siegeController && owner->siegeController->isAttackableByCatapult(myNumber))
|
||||
legalAction = true;
|
||||
break;
|
||||
case PossiblePlayerBattleAction::HEAL:
|
||||
|
@ -762,7 +762,7 @@ bool CShootingAnimation::init()
|
||||
const CCreature *shooterInfo = shooter->getCreature();
|
||||
|
||||
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
||||
shooterInfo = owner->siegeController->turretCreature();
|
||||
shooterInfo = owner->siegeController->getTurretCreature();
|
||||
|
||||
Point shooterPos;
|
||||
Point shotPos;
|
||||
@ -831,7 +831,7 @@ void CShootingAnimation::nextFrame()
|
||||
const CCreature *shooterInfo = attackingStack->getCreature();
|
||||
|
||||
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
||||
shooterInfo = owner->siegeController->turretCreature();
|
||||
shooterInfo = owner->siegeController->getTurretCreature();
|
||||
|
||||
// animation should be paused if there is an active projectile
|
||||
if ( stackAnimation(attackingStack)->getCurrentFrame() >= shooterInfo->animation.attackClimaxFrame )
|
||||
|
@ -94,7 +94,6 @@ void CBattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & b
|
||||
//waitForAnims(); //fixme: freezes game :?
|
||||
}
|
||||
|
||||
|
||||
void CBattleEffectsController::startAction(const BattleAction* action)
|
||||
{
|
||||
const CStack *stack = owner->curInt->cb->battleGetStackByID(action->stackNumber);
|
||||
@ -124,29 +123,23 @@ void CBattleEffectsController::startAction(const BattleAction* action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBattleEffectsController::showBattleEffects(SDL_Surface *to, const std::vector<const BattleEffect *> &battleEffects)
|
||||
void CBattleEffectsController::showBattlefieldObjects(SDL_Surface *to, const BattleHex & destTile)
|
||||
{
|
||||
for (auto & elem : battleEffects)
|
||||
{
|
||||
int currentFrame = static_cast<int>(floor(elem->currentFrame));
|
||||
currentFrame %= elem->animation->size();
|
||||
if (!elem.position.isValid() && destTile != BattleHex::HEX_AFTER_ALL)
|
||||
continue;
|
||||
|
||||
auto img = elem->animation->getImage(currentFrame);
|
||||
if (elem.position.isValid() && elem.position != destTile)
|
||||
continue;
|
||||
|
||||
SDL_Rect temp_rect = genRect(img->height(), img->width(), elem->x, elem->y);
|
||||
int currentFrame = static_cast<int>(floor(elem.currentFrame));
|
||||
currentFrame %= elem.animation->size();
|
||||
|
||||
auto img = elem.animation->getImage(currentFrame);
|
||||
|
||||
SDL_Rect temp_rect = genRect(img->height(), img->width(), elem.x, elem.y);
|
||||
|
||||
img->draw(to, &temp_rect, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleEffectsController::sortObjectsByHex(BattleObjectsByHex & sorted)
|
||||
{
|
||||
for (auto & battleEffect : battleEffects)
|
||||
{
|
||||
if (battleEffect.position.isValid())
|
||||
sorted.hex[battleEffect.position].effects.push_back(&battleEffect);
|
||||
else
|
||||
sorted.afterAll.effects.push_back(&battleEffect);
|
||||
}
|
||||
}
|
||||
|
@ -54,19 +54,21 @@ class CBattleEffectsController
|
||||
{
|
||||
CBattleInterface * owner;
|
||||
|
||||
std::vector<BattleEffect> battleEffects; //different animations to display on the screen like spell effects
|
||||
/// list of current effects that are being displayed on screen (spells & creature abilities)
|
||||
std::vector<BattleEffect> battleEffects;
|
||||
|
||||
public:
|
||||
CBattleEffectsController(CBattleInterface * owner);
|
||||
|
||||
void startAction(const BattleAction* action);
|
||||
void sortObjectsByHex(BattleObjectsByHex & sorted);
|
||||
|
||||
void displayCustomEffects(const std::vector<CustomEffectInfo> & customEffects);
|
||||
|
||||
void displayEffect(EBattleEffect::EBattleEffect effect, const BattleHex & destTile); //displays custom effect on the battlefield
|
||||
void displayEffect(EBattleEffect::EBattleEffect effect, uint32_t soundID, const BattleHex & destTile); //displays custom effect on the battlefield
|
||||
void battleTriggerEffect(const BattleTriggerEffect & bte);
|
||||
void showBattleEffects(SDL_Surface *to, const std::vector<const BattleEffect *> &battleEffects);
|
||||
|
||||
void showBattlefieldObjects(SDL_Surface *to, const BattleHex & destTile);
|
||||
|
||||
|
||||
friend class CEffectAnimation; // currently, battleEffects is largely managed by CEffectAnimation, TODO: move this logic into CBattleEffectsController
|
||||
|
@ -957,27 +957,36 @@ void CBattleInterface::show(SDL_Surface *to)
|
||||
activateStack();
|
||||
}
|
||||
|
||||
void CBattleInterface::showBattlefieldObjects(SDL_Surface *to, const BattleHex & location )
|
||||
{
|
||||
if (siegeController)
|
||||
siegeController->showBattlefieldObjects(to, location);
|
||||
obstacleController->showBattlefieldObjects(to, location);
|
||||
stacksController->showBattlefieldObjects(to, location);
|
||||
effectsController->showBattlefieldObjects(to, location);
|
||||
}
|
||||
|
||||
void CBattleInterface::showBattlefieldObjects(SDL_Surface *to)
|
||||
{
|
||||
auto showHexEntry = [&](BattleObjectsByHex::HexData & hex)
|
||||
{
|
||||
if (siegeController)
|
||||
siegeController->showPiecesOfWall(to, hex.walls);
|
||||
obstacleController->showObstacles(to, hex.obstacles);
|
||||
stacksController->showAliveStacks(to, hex.alive);
|
||||
effectsController->showBattleEffects(to, hex.effects);
|
||||
};
|
||||
//auto showHexEntry = [&](BattleObjectsByHex::HexData & hex)
|
||||
//{
|
||||
// if (siegeController)
|
||||
// siegeController->showPiecesOfWall(to, hex.walls);
|
||||
// obstacleController->showObstacles(to, hex.obstacles);
|
||||
// stacksController->showAliveStacks(to, hex.alive);
|
||||
// effectsController->showBattleEffects(to, hex.effects);
|
||||
//};
|
||||
|
||||
BattleObjectsByHex objects = sortObjectsByHex();
|
||||
//BattleObjectsByHex objects = sortObjectsByHex();
|
||||
|
||||
// dead stacks should be blit first
|
||||
stacksController->showStacks(to, objects.beforeAll.dead);
|
||||
for (auto & data : objects.hex)
|
||||
stacksController->showStacks(to, data.dead);
|
||||
stacksController->showStacks(to, objects.afterAll.dead);
|
||||
//stacksController->showStacks(to, objects.beforeAll.dead);
|
||||
//for (auto & data : objects.hex)
|
||||
// stacksController->showStacks(to, data.dead);
|
||||
//stacksController->showStacks(to, objects.afterAll.dead);
|
||||
|
||||
// display objects that must be blit before anything else (e.g. topmost walls)
|
||||
showHexEntry(objects.beforeAll);
|
||||
//showHexEntry(objects.beforeAll);
|
||||
|
||||
// show heroes after "beforeAll" - e.g. topmost wall in siege
|
||||
if (attackingHero)
|
||||
@ -987,11 +996,11 @@ void CBattleInterface::showBattlefieldObjects(SDL_Surface *to)
|
||||
|
||||
// actual blit of most of objects, hex by hex
|
||||
// NOTE: row-by-row blitting may be a better approach
|
||||
for (auto &data : objects.hex)
|
||||
showHexEntry(data);
|
||||
//for (auto &data : objects.hex)
|
||||
// showHexEntry(data);
|
||||
|
||||
// objects that must be blit *after* everything else - e.g. bottom tower or some spell effects
|
||||
showHexEntry(objects.afterAll);
|
||||
//showHexEntry(objects.afterAll);
|
||||
}
|
||||
|
||||
void CBattleInterface::showInterface(SDL_Surface *to)
|
||||
@ -1020,19 +1029,6 @@ void CBattleInterface::showInterface(SDL_Surface *to)
|
||||
}
|
||||
}
|
||||
|
||||
BattleObjectsByHex CBattleInterface::sortObjectsByHex()
|
||||
{
|
||||
BattleObjectsByHex sorted;
|
||||
|
||||
stacksController->sortObjectsByHex(sorted);
|
||||
obstacleController->sortObjectsByHex(sorted);
|
||||
effectsController->sortObjectsByHex(sorted);
|
||||
if (siegeController)
|
||||
siegeController->sortObjectsByHex(sorted);
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
void CBattleInterface::castThisSpell(SpellID spellID)
|
||||
{
|
||||
actionsController->castThisSpell(spellID);
|
||||
|
@ -70,28 +70,6 @@ struct StackAttackedInfo
|
||||
bool cloneKilled;
|
||||
};
|
||||
|
||||
|
||||
struct BattleObjectsByHex
|
||||
{
|
||||
typedef std::vector<int> TWallList;
|
||||
typedef std::vector<const CStack *> TStackList;
|
||||
typedef std::vector<const BattleEffect *> TEffectList;
|
||||
typedef std::vector<std::shared_ptr<const CObstacleInstance>> TObstacleList;
|
||||
|
||||
struct HexData
|
||||
{
|
||||
TWallList walls;
|
||||
TStackList dead;
|
||||
TStackList alive;
|
||||
TEffectList effects;
|
||||
TObstacleList obstacles;
|
||||
};
|
||||
|
||||
HexData beforeAll;
|
||||
HexData afterAll;
|
||||
std::array<HexData, GameConstants::BFIELD_SIZE> hex;
|
||||
};
|
||||
|
||||
/// Big class which handles the overall battle interface actions and it is also responsible for
|
||||
/// drawing everything correctly.
|
||||
class CBattleInterface : public WindowBase
|
||||
@ -128,8 +106,7 @@ private:
|
||||
void showInterface(SDL_Surface *to);
|
||||
|
||||
void showBattlefieldObjects(SDL_Surface *to);
|
||||
|
||||
BattleObjectsByHex sortObjectsByHex();
|
||||
void showBattlefieldObjects(SDL_Surface *to, const BattleHex & location );
|
||||
|
||||
void setHeroAnimation(ui8 side, int phase);
|
||||
public:
|
||||
|
@ -584,7 +584,7 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt
|
||||
|
||||
Point ret(-500, -500); //returned value
|
||||
if(stack && stack->initialPosition < 0) //creatures in turrets
|
||||
return cbi->siegeController->turretCreaturePosition(stack->initialPosition);
|
||||
return cbi->siegeController->getTurretCreaturePosition(stack->initialPosition);
|
||||
|
||||
static const Point basePos(-190, -139); // position of creature in topleft corner
|
||||
static const int imageShiftX = 30; // X offset to base pos for facing right stacks, negative for facing left
|
||||
|
@ -145,7 +145,11 @@ public:
|
||||
//CStack * ourStack;
|
||||
bool strictHovered; //for determining if hex is hovered by mouse (this is different problem than hex's graphic hovering)
|
||||
CBattleInterface * myInterface; //interface that owns me
|
||||
static Point getXYUnitAnim(BattleHex hexNum, const CStack * creature, CBattleInterface * cbi); //returns (x, y) of left top corner of animation
|
||||
|
||||
/// returns (x, y) of left top corner of animation
|
||||
/// FIXME: move someplace else?
|
||||
/// FIXME: some usages should be replaced with creAnims->pos?
|
||||
static Point getXYUnitAnim(BattleHex hexNum, const CStack * creature, CBattleInterface * cbi);
|
||||
|
||||
//for user interactions
|
||||
void hover (bool on) override;
|
||||
|
@ -122,10 +122,19 @@ void CBattleObstacleController::showAbsoluteObstacles(SDL_Surface * to)
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleObstacleController::showObstacles(SDL_Surface * to, std::vector<std::shared_ptr<const CObstacleInstance>> & obstacles)
|
||||
void CBattleObstacleController::showBattlefieldObjects(SDL_Surface *to, const BattleHex & location )
|
||||
{
|
||||
for(auto & obstacle : obstacles)
|
||||
for (auto &obstacle : owner->curInt->cb->battleGetAllObstacles())
|
||||
{
|
||||
if (obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
|
||||
continue;
|
||||
|
||||
if (obstacle->obstacleType != CObstacleInstance::MOAT)
|
||||
continue;
|
||||
|
||||
if ( obstacle->pos != location)
|
||||
continue;
|
||||
|
||||
auto img = getObstacleImage(*obstacle);
|
||||
if(img)
|
||||
{
|
||||
@ -135,22 +144,6 @@ void CBattleObstacleController::showObstacles(SDL_Surface * to, std::vector<std:
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
struct SDL_Surface;
|
||||
struct BattleObjectsByHex;
|
||||
struct BattleHex;
|
||||
class IImage;
|
||||
class CAnimation;
|
||||
class CBattleInterface;
|
||||
@ -24,6 +25,10 @@ class CBattleObstacleController
|
||||
CBattleInterface * owner;
|
||||
|
||||
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
|
||||
|
||||
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
|
||||
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
|
||||
|
||||
public:
|
||||
CBattleObstacleController(CBattleInterface * owner);
|
||||
|
||||
@ -31,11 +36,7 @@ public:
|
||||
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 showBattlefieldObjects(SDL_Surface *to, const BattleHex & location );
|
||||
|
||||
void redrawBackgroundWithHexes(SDL_Surface * to);
|
||||
};
|
||||
|
@ -58,7 +58,7 @@ void CBattleProjectileController::initStackProjectile(const CStack * stack)
|
||||
{
|
||||
const CCreature * creature;//creature whose shots should be loaded
|
||||
if(stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
|
||||
creature = owner->siegeController->turretCreature();
|
||||
creature = owner->siegeController->getTurretCreature();
|
||||
else
|
||||
creature = stack->getCreature();
|
||||
|
||||
@ -208,7 +208,7 @@ void CBattleProjectileController::createProjectile(const CStack * shooter, const
|
||||
const CCreature *shooterInfo = shooter->getCreature();
|
||||
|
||||
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
||||
shooterInfo = owner->siegeController->turretCreature();
|
||||
shooterInfo = owner->siegeController->getTurretCreature();
|
||||
|
||||
if(!shooterInfo->animation.missleFrameAngles.size())
|
||||
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead..."
|
||||
|
@ -10,51 +10,37 @@
|
||||
#include "StdInc.h"
|
||||
#include "CBattleSiegeController.h"
|
||||
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../../lib/NetPacks.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../CCallback.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../../lib/CStack.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "CBattleInterface.h"
|
||||
#include "CBattleAnimations.h"
|
||||
#include "CBattleInterface.h"
|
||||
#include "CBattleInterfaceClasses.h"
|
||||
#include "CBattleStacksController.h"
|
||||
|
||||
CBattleSiegeController::~CBattleSiegeController()
|
||||
{
|
||||
auto gateState = owner->curInt->cb->battleGetGateState();
|
||||
for (int g = 0; g < ARRAY_COUNT(walls); ++g)
|
||||
{
|
||||
if (g != EWallVisual::GATE || (gateState != EGateState::NONE && gateState != EGateState::CLOSED && gateState != EGateState::BLOCKED))
|
||||
SDL_FreeSurface(walls[g]);
|
||||
}
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
|
||||
SDL_FreeSurface(moatSurface);
|
||||
SDL_FreeSurface(mlipSurface);
|
||||
}
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/NetPacks.h"
|
||||
#include "../../lib/CStack.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
|
||||
std::string CBattleSiegeController::getSiegeName(ui16 what) const
|
||||
{
|
||||
return getSiegeName(what, EWallState::INTACT);
|
||||
}
|
||||
|
||||
std::string CBattleSiegeController::getSiegeName(ui16 what, int state) const
|
||||
std::string CBattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual what, EWallState::EWallState state) const
|
||||
{
|
||||
auto getImageIndex = [&]() -> int
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case EWallState::INTACT : return 1;
|
||||
case EWallState::INTACT :
|
||||
return 1;
|
||||
case EWallState::DAMAGED :
|
||||
if(what == 2 || what == 3 || what == 8) // towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
|
||||
// towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
|
||||
if(what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
case EWallState::DESTROYED :
|
||||
if (what == 2 || what == 3 || what == 8)
|
||||
if (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER)
|
||||
return 2;
|
||||
else
|
||||
return 3;
|
||||
@ -67,8 +53,6 @@ std::string CBattleSiegeController::getSiegeName(ui16 what, int state) const
|
||||
|
||||
switch(what)
|
||||
{
|
||||
case EWallVisual::BACKGROUND:
|
||||
return prefix + "BACK.BMP";
|
||||
case EWallVisual::BACKGROUND_WALL:
|
||||
{
|
||||
switch(town->town->faction->index)
|
||||
@ -119,38 +103,64 @@ std::string CBattleSiegeController::getSiegeName(ui16 what, int state) const
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleSiegeController::printPartOfWall(SDL_Surface *to, int what)
|
||||
void CBattleSiegeController::showWallPiece(SDL_Surface *to, EWallVisual::EWallVisual what)
|
||||
{
|
||||
Point pos = Point(-1, -1);
|
||||
auto & ci = town->town->clientInfo;
|
||||
auto const & pos = ci.siegePositions[what];
|
||||
|
||||
if (vstd::iswithin(what, 1, 17))
|
||||
{
|
||||
pos.x = ci.siegePositions[what].x + owner->pos.x;
|
||||
pos.y = ci.siegePositions[what].y + owner->pos.y;
|
||||
}
|
||||
|
||||
if (town->town->faction->index == ETownType::TOWER
|
||||
&& (what == EWallVisual::MOAT || what == EWallVisual::BACKGROUND_MOAT))
|
||||
return; // no moat in Tower. TODO: remove hardcode somehow?
|
||||
|
||||
if (pos.x != -1)
|
||||
{
|
||||
//gate have no displayed bitmap when drawbridge is raised
|
||||
if (what == EWallVisual::GATE)
|
||||
{
|
||||
auto gateState = owner->curInt->cb->battleGetGateState();
|
||||
if (gateState != EGateState::OPENED && gateState != EGateState::DESTROYED)
|
||||
return;
|
||||
}
|
||||
|
||||
blitAt(walls[what], pos.x, pos.y, to);
|
||||
}
|
||||
wallPieceImages[what]->draw(to, pos.x + owner->pos.x, pos.y + owner->pos.y);
|
||||
}
|
||||
|
||||
std::string CBattleSiegeController::getBattleBackgroundName()
|
||||
std::string CBattleSiegeController::getBattleBackgroundName() const
|
||||
{
|
||||
return getSiegeName(0);
|
||||
const std::string & prefix = town->town->clientInfo.siegePrefix;
|
||||
return prefix + "BACK.BMP";
|
||||
}
|
||||
|
||||
bool CBattleSiegeController::getWallPieceExistance(EWallVisual::EWallVisual what) const
|
||||
{
|
||||
//FIXME: use this instead of buildings test?
|
||||
//ui8 siegeLevel = owner->curInt->cb->battleGetSiegeLevel();
|
||||
|
||||
bool isMoat = (what == EWallVisual::BACKGROUND_MOAT || what == EWallVisual::MOAT);
|
||||
bool isKeep = what == EWallVisual::KEEP_BATTLEMENT;
|
||||
bool isTower = (what == EWallVisual::UPPER_BATTLEMENT || what == EWallVisual::BOTTOM_BATTLEMENT);
|
||||
|
||||
bool hasMoat = town->hasBuilt(BuildingID::CITADEL) && town->town->faction->index != ETownType::TOWER;
|
||||
bool hasKeep = town->hasBuilt(BuildingID::CITADEL);
|
||||
bool hasTower = town->hasBuilt(BuildingID::CASTLE);
|
||||
|
||||
if ( isMoat ) return hasMoat;
|
||||
if ( isKeep ) return hasKeep;
|
||||
if ( isTower ) return hasTower;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BattleHex CBattleSiegeController::getWallPiecePosition(EWallVisual::EWallVisual what) const
|
||||
{
|
||||
static const std::array<BattleHex, 18> wallsPositions = {
|
||||
BattleHex::INVALID, // background, handled separately
|
||||
BattleHex::HEX_BEFORE_ALL,
|
||||
135,
|
||||
BattleHex::HEX_AFTER_ALL,
|
||||
182,
|
||||
130,
|
||||
78,
|
||||
12,
|
||||
BattleHex::HEX_BEFORE_ALL,
|
||||
94,
|
||||
112,
|
||||
165,
|
||||
45,
|
||||
BattleHex::INVALID, //moat, printed as obstacle // BattleHex::HEX_BEFORE_ALL,
|
||||
BattleHex::INVALID, //moat, printed as obstacle
|
||||
135,
|
||||
BattleHex::HEX_AFTER_ALL,
|
||||
BattleHex::HEX_BEFORE_ALL
|
||||
};
|
||||
|
||||
return wallsPositions[what];
|
||||
}
|
||||
|
||||
CBattleSiegeController::CBattleSiegeController(CBattleInterface * owner, const CGTownInstance *siegeTown):
|
||||
@ -159,35 +169,37 @@ CBattleSiegeController::CBattleSiegeController(CBattleInterface * owner, const C
|
||||
{
|
||||
assert(owner->fieldController.get() == nullptr); // must be created after this
|
||||
|
||||
moatSurface = BitmapHandler::loadBitmap( getSiegeName(13) );
|
||||
mlipSurface = BitmapHandler::loadBitmap( getSiegeName(14) );
|
||||
|
||||
for (int g = 0; g < ARRAY_COUNT(walls); ++g)
|
||||
for (int g = 0; g < wallPieceImages.size(); ++g)
|
||||
{
|
||||
if (g != EWallVisual::GATE)
|
||||
walls[g] = BitmapHandler::loadBitmap(getSiegeName(g));
|
||||
if ( g == EWallVisual::GATE ) // gate is initially closed and has no image to display in this state
|
||||
continue;
|
||||
|
||||
if ( !getWallPieceExistance(EWallVisual::EWallVisual(g)) )
|
||||
continue;
|
||||
|
||||
wallPieceImages[g] = IImage::createFromFile(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::INTACT));
|
||||
}
|
||||
}
|
||||
|
||||
const CCreature *CBattleSiegeController::turretCreature()
|
||||
const CCreature *CBattleSiegeController::getTurretCreature() const
|
||||
{
|
||||
return CGI->creh->objects[town->town->clientInfo.siegeShooter];
|
||||
}
|
||||
|
||||
Point CBattleSiegeController::turretCreaturePosition( BattleHex position )
|
||||
Point CBattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
|
||||
{
|
||||
// Turret positions are read out of the config/wall_pos.txt
|
||||
int posID = 0;
|
||||
switch (position)
|
||||
{
|
||||
case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature
|
||||
posID = 18;
|
||||
posID = EWallVisual::CREATURE_KEEP;
|
||||
break;
|
||||
case BattleHex::CASTLE_BOTTOM_TOWER: // bottom creature
|
||||
posID = 19;
|
||||
posID = EWallVisual::CREATURE_BOTTOM_TOWER;
|
||||
break;
|
||||
case BattleHex::CASTLE_UPPER_TOWER: // upper creature
|
||||
posID = 20;
|
||||
posID = EWallVisual::CREATURE_UPPER_TOWER;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -203,12 +215,11 @@ Point CBattleSiegeController::turretCreaturePosition( BattleHex position )
|
||||
return Point(0,0);
|
||||
}
|
||||
|
||||
|
||||
void CBattleSiegeController::gateStateChanged(const EGateState state)
|
||||
{
|
||||
auto oldState = owner->curInt->cb->battleGetGateState();
|
||||
bool playSound = false;
|
||||
int stateId = EWallState::NONE;
|
||||
auto stateId = EWallState::NONE;
|
||||
switch(state)
|
||||
{
|
||||
case EGateState::CLOSED:
|
||||
@ -229,102 +240,87 @@ void CBattleSiegeController::gateStateChanged(const EGateState state)
|
||||
}
|
||||
|
||||
if (oldState != EGateState::NONE && oldState != EGateState::CLOSED && oldState != EGateState::BLOCKED)
|
||||
SDL_FreeSurface(walls[EWallVisual::GATE]);
|
||||
wallPieceImages[EWallVisual::GATE] = nullptr;
|
||||
|
||||
if (stateId != EWallState::NONE)
|
||||
walls[EWallVisual::GATE] = BitmapHandler::loadBitmap(getSiegeName(EWallVisual::GATE, stateId));
|
||||
wallPieceImages[EWallVisual::GATE] = IImage::createFromFile(getWallPieceImageName(EWallVisual::GATE, stateId));
|
||||
|
||||
if (playSound)
|
||||
CCS->soundh->playSound(soundBase::DRAWBRG);
|
||||
}
|
||||
|
||||
void CBattleSiegeController::showAbsoluteObstacles(SDL_Surface * to)
|
||||
{
|
||||
ui8 siegeLevel = owner->curInt->cb->battleGetSiegeLevel();
|
||||
if (siegeLevel >= 2) //citadel or castle
|
||||
auto & info = town->town->clientInfo;
|
||||
|
||||
if (getWallPieceExistance(EWallVisual::MOAT))
|
||||
{
|
||||
//print moat/mlip
|
||||
auto const & pos = info.siegePositions[EWallVisual::MOAT];
|
||||
wallPieceImages[EWallVisual::MOAT]->draw(to, pos.x + owner->pos.x, pos.y + owner->pos.y);
|
||||
}
|
||||
|
||||
auto & info = town->town->clientInfo;
|
||||
Point moatPos(info.siegePositions[13].x, info.siegePositions[13].y);
|
||||
Point mlipPos(info.siegePositions[14].x, info.siegePositions[14].y);
|
||||
|
||||
if (moatSurface) //eg. tower has no moat
|
||||
blitAt(moatSurface, moatPos.x + owner->pos.x, moatPos.y + owner->pos.y, to);
|
||||
if (mlipSurface) //eg. tower has no mlip
|
||||
blitAt(mlipSurface, mlipPos.x + owner->pos.x, mlipPos.y + owner->pos.y, to);
|
||||
|
||||
printPartOfWall(to, EWallVisual::BACKGROUND_MOAT);
|
||||
if (getWallPieceExistance(EWallVisual::BACKGROUND_MOAT))
|
||||
{
|
||||
auto const & pos = info.siegePositions[EWallVisual::BACKGROUND_MOAT];
|
||||
wallPieceImages[EWallVisual::BACKGROUND_MOAT]->draw(to, pos.x + owner->pos.x, pos.y + owner->pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleSiegeController::sortObjectsByHex(BattleObjectsByHex & sorted)
|
||||
void CBattleSiegeController::showBattlefieldObjects(SDL_Surface *to, const BattleHex & location )
|
||||
{
|
||||
sorted.beforeAll.walls.push_back(EWallVisual::BACKGROUND_WALL);
|
||||
sorted.hex[135].walls.push_back(EWallVisual::KEEP);
|
||||
sorted.afterAll.walls.push_back(EWallVisual::BOTTOM_TOWER);
|
||||
sorted.hex[182].walls.push_back(EWallVisual::BOTTOM_WALL);
|
||||
sorted.hex[130].walls.push_back(EWallVisual::WALL_BELLOW_GATE);
|
||||
sorted.hex[78].walls.push_back(EWallVisual::WALL_OVER_GATE);
|
||||
sorted.hex[12].walls.push_back(EWallVisual::UPPER_WALL);
|
||||
sorted.beforeAll.walls.push_back(EWallVisual::UPPER_TOWER);
|
||||
sorted.hex[94].walls.push_back(EWallVisual::GATE);
|
||||
sorted.hex[112].walls.push_back(EWallVisual::GATE_ARCH);
|
||||
sorted.hex[165].walls.push_back(EWallVisual::BOTTOM_STATIC_WALL);
|
||||
sorted.hex[45].walls.push_back(EWallVisual::UPPER_STATIC_WALL);
|
||||
|
||||
if (town->hasBuilt(BuildingID::CITADEL))
|
||||
for (int i = EWallVisual::WALL_FIRST; i <= EWallVisual::WALL_LAST; ++i)
|
||||
{
|
||||
sorted.beforeAll.walls.push_back(EWallVisual::MOAT);
|
||||
//sorted.beforeAll.walls.push_back(EWallVisual::BACKGROUND_MOAT); // blit as absolute obstacle
|
||||
sorted.hex[135].walls.push_back(EWallVisual::KEEP_BATTLEMENT);
|
||||
}
|
||||
if (town->hasBuilt(BuildingID::CASTLE))
|
||||
{
|
||||
sorted.afterAll.walls.push_back(EWallVisual::BOTTOM_BATTLEMENT);
|
||||
sorted.beforeAll.walls.push_back(EWallVisual::UPPER_BATTLEMENT);
|
||||
}
|
||||
}
|
||||
auto wallPiece = EWallVisual::EWallVisual(i);
|
||||
|
||||
if ( !getWallPieceExistance(wallPiece))
|
||||
continue;
|
||||
|
||||
void CBattleSiegeController::showPiecesOfWall(SDL_Surface *to, std::vector<int> pieces)
|
||||
{
|
||||
for (auto piece : pieces)
|
||||
{
|
||||
if (piece < 15) // not a tower - just print
|
||||
printPartOfWall(to, piece);
|
||||
else // tower. find if tower is built and not destroyed - stack is present
|
||||
if ( getWallPiecePosition(wallPiece) != location)
|
||||
continue;
|
||||
|
||||
if (wallPiece != EWallVisual::KEEP_BATTLEMENT &&
|
||||
wallPiece != EWallVisual::BOTTOM_BATTLEMENT &&
|
||||
wallPiece != EWallVisual::UPPER_BATTLEMENT)
|
||||
{
|
||||
// PieceID StackID
|
||||
// 15 = keep, -2
|
||||
// 16 = lower, -3
|
||||
// 17 = upper, -4
|
||||
showWallPiece(to, wallPiece);
|
||||
continue;
|
||||
}
|
||||
|
||||
// tower. check if tower is alive - stack is found
|
||||
int stackPos = 13 - piece;
|
||||
// tower. check if tower is alive - stack is found
|
||||
BattleHex stackPos;
|
||||
switch(wallPiece)
|
||||
{
|
||||
case EWallVisual::KEEP_BATTLEMENT:
|
||||
stackPos = BattleHex::CASTLE_CENTRAL_TOWER;
|
||||
break;
|
||||
case EWallVisual::BOTTOM_BATTLEMENT:
|
||||
stackPos = BattleHex::CASTLE_BOTTOM_TOWER;
|
||||
break;
|
||||
case EWallVisual::UPPER_BATTLEMENT:
|
||||
stackPos = BattleHex::CASTLE_UPPER_TOWER;
|
||||
break;
|
||||
}
|
||||
|
||||
const CStack *turret = nullptr;
|
||||
const CStack *turret = nullptr;
|
||||
|
||||
for (auto & stack : owner->curInt->cb->battleGetAllStacks(true))
|
||||
for (auto & stack : owner->curInt->cb->battleGetAllStacks(true))
|
||||
{
|
||||
if(stack->initialPosition == stackPos)
|
||||
{
|
||||
if(stack->initialPosition == stackPos)
|
||||
{
|
||||
turret = stack;
|
||||
break;
|
||||
}
|
||||
turret = stack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (turret)
|
||||
{
|
||||
std::vector<const CStack *> stackList(1, turret);
|
||||
owner->stacksController->showStacks(to, stackList);
|
||||
printPartOfWall(to, piece);
|
||||
}
|
||||
if (turret)
|
||||
{
|
||||
owner->stacksController->showStack(to, turret);
|
||||
showWallPiece(to, wallPiece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CBattleSiegeController::isCatapultAttackable(BattleHex hex) const
|
||||
bool CBattleSiegeController::isAttackableByCatapult(BattleHex hex) const
|
||||
{
|
||||
if (owner->tacticsMode)
|
||||
return false;
|
||||
@ -362,13 +358,13 @@ void CBattleSiegeController::stackIsCatapulting(const CatapultAttack & ca)
|
||||
|
||||
for (auto attackInfo : ca.attackedParts)
|
||||
{
|
||||
int wallId = attackInfo.attackedPart + 2;
|
||||
int wallId = attackInfo.attackedPart + EWallVisual::DESTRUCTIBLE_FIRST;
|
||||
//gate state changing handled separately
|
||||
if (wallId == EWallVisual::GATE)
|
||||
continue;
|
||||
|
||||
SDL_FreeSurface(walls[wallId]);
|
||||
walls[wallId] = BitmapHandler::loadBitmap(
|
||||
getSiegeName(wallId, owner->curInt->cb->battleGetWallState(attackInfo.attackedPart)));
|
||||
auto wallState = EWallState::EWallState(owner->curInt->cb->battleGetWallState(attackInfo.attackedPart));
|
||||
|
||||
wallPieceImages[wallId] = IImage::createFromFile(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState));
|
||||
}
|
||||
}
|
||||
|
@ -10,22 +10,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
|
||||
struct BattleObjectsByHex;
|
||||
struct CatapultAttack;
|
||||
struct SDL_Surface;
|
||||
struct BattleHex;
|
||||
struct Point;
|
||||
struct SDL_Surface;
|
||||
class CGTownInstance;
|
||||
class CBattleInterface;
|
||||
class CCreature;
|
||||
class IImage;
|
||||
|
||||
namespace EWallVisual
|
||||
{
|
||||
enum EWallVisual
|
||||
{
|
||||
BACKGROUND = 0,
|
||||
BACKGROUND_WALL = 1,
|
||||
BACKGROUND,
|
||||
BACKGROUND_WALL,
|
||||
|
||||
KEEP,
|
||||
BOTTOM_TOWER,
|
||||
BOTTOM_WALL,
|
||||
@ -34,6 +35,7 @@ namespace EWallVisual
|
||||
UPPER_WALL,
|
||||
UPPER_TOWER,
|
||||
GATE,
|
||||
|
||||
GATE_ARCH,
|
||||
BOTTOM_STATIC_WALL,
|
||||
UPPER_STATIC_WALL,
|
||||
@ -41,7 +43,18 @@ namespace EWallVisual
|
||||
BACKGROUND_MOAT,
|
||||
KEEP_BATTLEMENT,
|
||||
BOTTOM_BATTLEMENT,
|
||||
UPPER_BATTLEMENT
|
||||
UPPER_BATTLEMENT,
|
||||
|
||||
CREATURE_KEEP,
|
||||
CREATURE_BOTTOM_TOWER,
|
||||
CREATURE_UPPER_TOWER,
|
||||
|
||||
WALL_FIRST = BACKGROUND_WALL,
|
||||
WALL_LAST = UPPER_BATTLEMENT,
|
||||
|
||||
// these entries are mapped to EWallPart enum
|
||||
DESTRUCTIBLE_FIRST = KEEP,
|
||||
DESTRUCTIBLE_LAST = GATE,
|
||||
};
|
||||
}
|
||||
|
||||
@ -49,32 +62,39 @@ class CBattleSiegeController
|
||||
{
|
||||
CBattleInterface * owner;
|
||||
|
||||
SDL_Surface *moatSurface;
|
||||
SDL_Surface *mlipSurface;
|
||||
/// besieged town
|
||||
const CGTownInstance *town;
|
||||
|
||||
SDL_Surface* walls[18];
|
||||
const CGTownInstance *town; //besieged town
|
||||
/// sections of castle walls, in their currently visible state
|
||||
std::array<std::shared_ptr<IImage>, EWallVisual::WALL_LAST + 1> wallPieceImages;
|
||||
|
||||
std::string getSiegeName(ui16 what) const;
|
||||
std::string getSiegeName(ui16 what, int state) const; // state uses EWallState enum
|
||||
/// return URI for image for a wall piece
|
||||
std::string getWallPieceImageName(EWallVisual::EWallVisual what, EWallState::EWallState state) const;
|
||||
|
||||
void printPartOfWall(SDL_Surface *to, int what);
|
||||
/// returns BattleHex to which chosen wall piece is bound
|
||||
BattleHex getWallPiecePosition(EWallVisual::EWallVisual what) const;
|
||||
|
||||
/// returns true if chosen wall piece should be present in current battle
|
||||
bool getWallPieceExistance(EWallVisual::EWallVisual what) const;
|
||||
|
||||
void showWallPiece(SDL_Surface *to, EWallVisual::EWallVisual what);
|
||||
|
||||
public:
|
||||
CBattleSiegeController(CBattleInterface * owner, const CGTownInstance *siegeTown);
|
||||
~CBattleSiegeController();
|
||||
|
||||
void showPiecesOfWall(SDL_Surface *to, std::vector<int> pieces);
|
||||
|
||||
std::string getBattleBackgroundName();
|
||||
const CCreature *turretCreature();
|
||||
Point turretCreaturePosition( BattleHex position );
|
||||
|
||||
/// call-ins from server
|
||||
void gateStateChanged(const EGateState state);
|
||||
void stackIsCatapulting(const CatapultAttack & ca);
|
||||
|
||||
/// call-ins from other battle controllers
|
||||
void showAbsoluteObstacles(SDL_Surface * to);
|
||||
bool isCatapultAttackable(BattleHex hex) const; //returns true if given tile can be attacked by catapult
|
||||
void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
|
||||
void showBattlefieldObjects(SDL_Surface *to, const BattleHex & location );
|
||||
|
||||
/// queries from other battle controllers
|
||||
bool isAttackableByCatapult(BattleHex hex) const;
|
||||
std::string getBattleBackgroundName() const;
|
||||
const CCreature *getTurretCreature() const;
|
||||
Point getTurretCreaturePosition( BattleHex position ) const;
|
||||
|
||||
|
||||
void sortObjectsByHex(BattleObjectsByHex & sorted);
|
||||
};
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "CBattleEffectsController.h"
|
||||
#include "CBattleProjectileController.h"
|
||||
#include "CBattleControlPanel.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
@ -56,42 +56,24 @@ static void onAnimationFinished(const CStack *stack, std::weak_ptr<CCreatureAnim
|
||||
animation->onAnimationReset += std::bind(&onAnimationFinished, stack, anim);
|
||||
}
|
||||
|
||||
static void transformPalette(SDL_Surface *surf, double rCor, double gCor, double bCor)
|
||||
{
|
||||
SDL_Color *colorsToChange = surf->format->palette->colors;
|
||||
for (int g=0; g<surf->format->palette->ncolors; ++g)
|
||||
{
|
||||
SDL_Color *color = &colorsToChange[g];
|
||||
if (color->b != 132 &&
|
||||
color->g != 231 &&
|
||||
color->r != 255) //it's not yellow border
|
||||
{
|
||||
color->r = static_cast<Uint8>(color->r * rCor);
|
||||
color->g = static_cast<Uint8>(color->g * gCor);
|
||||
color->b = static_cast<Uint8>(color->b * bCor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBattleStacksController::CBattleStacksController(CBattleInterface * owner):
|
||||
owner(owner)
|
||||
{
|
||||
//preparing graphics for displaying amounts of creatures
|
||||
amountNormal = BitmapHandler::loadBitmap("CMNUMWIN.BMP");
|
||||
CSDL_Ext::alphaTransform(amountNormal);
|
||||
transformPalette(amountNormal, 0.59, 0.19, 0.93);
|
||||
amountNormal = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
amountPositive = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
amountNegative = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
amountEffNeutral = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
|
||||
amountPositive = BitmapHandler::loadBitmap("CMNUMWIN.BMP");
|
||||
CSDL_Ext::alphaTransform(amountPositive);
|
||||
transformPalette(amountPositive, 0.18, 1.00, 0.18);
|
||||
ColorShifterAddMulExcept shifterNormal ({0,0,0,0}, {150, 48, 237, 255}, {132, 231, 255, 255});
|
||||
ColorShifterAddMulExcept shifterPositive({0,0,0,0}, { 45, 255, 45, 255}, {132, 231, 255, 255});
|
||||
ColorShifterAddMulExcept shifterNegative({0,0,0,0}, {255, 45, 45, 255}, {132, 231, 255, 255});
|
||||
ColorShifterAddMulExcept shifterNeutral ({0,0,0,0}, {255, 255, 45, 255}, {132, 231, 255, 255});
|
||||
|
||||
amountNegative = BitmapHandler::loadBitmap("CMNUMWIN.BMP");
|
||||
CSDL_Ext::alphaTransform(amountNegative);
|
||||
transformPalette(amountNegative, 1.00, 0.18, 0.18);
|
||||
|
||||
amountEffNeutral = BitmapHandler::loadBitmap("CMNUMWIN.BMP");
|
||||
CSDL_Ext::alphaTransform(amountEffNeutral);
|
||||
transformPalette(amountEffNeutral, 1.00, 1.00, 0.18);
|
||||
amountNormal->adjustPalette(&shifterNormal);
|
||||
amountPositive->adjustPalette(&shifterPositive);
|
||||
amountNegative->adjustPalette(&shifterNegative);
|
||||
amountEffNeutral->adjustPalette(&shifterNeutral);
|
||||
|
||||
std::vector<const CStack*> stacks = owner->curInt->cb->battleGetAllStacks(true);
|
||||
for(const CStack * s : stacks)
|
||||
@ -100,15 +82,7 @@ CBattleStacksController::CBattleStacksController(CBattleInterface * owner):
|
||||
}
|
||||
}
|
||||
|
||||
CBattleStacksController::~CBattleStacksController()
|
||||
{
|
||||
SDL_FreeSurface(amountNormal);
|
||||
SDL_FreeSurface(amountNegative);
|
||||
SDL_FreeSurface(amountPositive);
|
||||
SDL_FreeSurface(amountEffNeutral);
|
||||
}
|
||||
|
||||
void CBattleStacksController::sortObjectsByHex(BattleObjectsByHex & sorted)
|
||||
void CBattleStacksController::showBattlefieldObjects(SDL_Surface *to, const BattleHex & location )
|
||||
{
|
||||
auto getCurrentPosition = [&](const CStack *stack) -> BattleHex
|
||||
{
|
||||
@ -127,18 +101,15 @@ void CBattleStacksController::sortObjectsByHex(BattleObjectsByHex & sorted)
|
||||
return stack->getPosition();
|
||||
};
|
||||
|
||||
auto stacks = owner->curInt->cb->battleGetStacksIf([](const CStack *s)
|
||||
{
|
||||
return !s->isTurret();
|
||||
});
|
||||
auto stacks = owner->curInt->cb->battleGetAllStacks(true);
|
||||
|
||||
for (auto & stack : stacks)
|
||||
{
|
||||
if (creAnims.find(stack->ID) == creAnims.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
|
||||
if (stack->initialPosition < 0) // turret shooters are handled separately
|
||||
continue;
|
||||
//if (stack->initialPosition < 0) // turret shooters are handled separately
|
||||
// continue;
|
||||
|
||||
//FIXME: hack to ignore ghost stacks
|
||||
if ((creAnims[stack->ID]->getType() == CCreatureAnim::DEAD || creAnims[stack->ID]->getType() == CCreatureAnim::HOLDING) && stack->isGhost())
|
||||
@ -146,24 +117,34 @@ void CBattleStacksController::sortObjectsByHex(BattleObjectsByHex & sorted)
|
||||
|
||||
if (creAnims[stack->ID]->isDead())
|
||||
{
|
||||
sorted.hex[stack->getPosition()].dead.push_back(stack);
|
||||
//if ( location == stack->getPosition() )
|
||||
if ( location == BattleHex::HEX_BEFORE_ALL ) //FIXME: any cases when this won't work?
|
||||
{};
|
||||
continue;
|
||||
}
|
||||
|
||||
// standing - blit at current position
|
||||
if (!creAnims[stack->ID]->isMoving())
|
||||
{
|
||||
sorted.hex[stack->getPosition()].alive.push_back(stack);
|
||||
if ( location == stack->getPosition() )
|
||||
{};
|
||||
continue;
|
||||
}
|
||||
|
||||
// flying creature - just blit them over everyone else
|
||||
if (stack->hasBonusOfType(Bonus::FLYING))
|
||||
{
|
||||
sorted.afterAll.alive.push_back(stack);
|
||||
if ( location == BattleHex::HEX_AFTER_ALL)
|
||||
{};
|
||||
continue;
|
||||
}
|
||||
|
||||
sorted.hex[getCurrentPosition(stack)].alive.push_back(stack);
|
||||
// else - unit moving on ground
|
||||
{
|
||||
if ( location == getCurrentPosition(stack) )
|
||||
{};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +165,7 @@ void CBattleStacksController::stackReset(const CStack * stack)
|
||||
|
||||
if (stack->isClone())
|
||||
{
|
||||
ColorShifterDeepBlue shifter;
|
||||
auto shifter = ColorShifterAddMul::deepBlue();
|
||||
animation->shiftColor(&shifter);
|
||||
}
|
||||
|
||||
@ -201,12 +182,12 @@ void CBattleStacksController::stackAdded(const CStack * stack)
|
||||
{
|
||||
assert(owner->siegeController);
|
||||
|
||||
const CCreature *turretCreature = owner->siegeController->turretCreature();
|
||||
const CCreature *turretCreature = owner->siegeController->getTurretCreature();
|
||||
|
||||
creAnims[stack->ID] = AnimationControls::getAnimation(turretCreature);
|
||||
creAnims[stack->ID]->pos.h = 225;
|
||||
|
||||
coords = owner->siegeController->turretCreaturePosition(stack->initialPosition);
|
||||
coords = owner->siegeController->getTurretCreaturePosition(stack->initialPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -263,7 +244,7 @@ void CBattleStacksController::setHoveredStack(const CStack *stack)
|
||||
mouseHoveredStack = nullptr;
|
||||
}
|
||||
|
||||
void CBattleStacksController::showAliveStacks(SDL_Surface *to, std::vector<const CStack *> stacks)
|
||||
bool CBattleStacksController::stackNeedsAmountBox(const CStack * stack)
|
||||
{
|
||||
BattleHex currentActionTarget;
|
||||
if(owner->curInt->curAction)
|
||||
@ -273,39 +254,41 @@ void CBattleStacksController::showAliveStacks(SDL_Surface *to, std::vector<const
|
||||
currentActionTarget = target.at(0).hexValue;
|
||||
}
|
||||
|
||||
auto isAmountBoxVisible = [&](const CStack *stack) -> bool
|
||||
if(!stack->alive())
|
||||
return false;
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1) //do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
|
||||
return false;
|
||||
|
||||
if(stack->getCount() == 0) //hide box when target is going to die anyway - do not display "0 creatures"
|
||||
return false;
|
||||
|
||||
for(auto anim : pendingAnims) //no matter what other conditions below are, hide box when creature is playing hit animation
|
||||
{
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1) //do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
|
||||
auto hitAnimation = dynamic_cast<CDefenceAnimation*>(anim.first);
|
||||
if(hitAnimation && (hitAnimation->stack->ID == stack->ID)) //we process only "current creature" as other creatures will be processed reliably on their own iteration
|
||||
return false;
|
||||
}
|
||||
|
||||
if(stack->getCount() == 0) //hide box when target is going to die anyway - do not display "0 creatures"
|
||||
return false;
|
||||
|
||||
for(auto anim : pendingAnims) //no matter what other conditions below are, hide box when creature is playing hit animation
|
||||
if(owner->curInt->curAction)
|
||||
{
|
||||
if(owner->curInt->curAction->stackNumber == stack->ID) //stack is currently taking action (is not a target of another creature's action etc)
|
||||
{
|
||||
auto hitAnimation = dynamic_cast<CDefenceAnimation*>(anim.first);
|
||||
if(hitAnimation && (hitAnimation->stack->ID == stack->ID)) //we process only "current creature" as other creatures will be processed reliably on their own iteration
|
||||
if(owner->curInt->curAction->actionType == EActionType::WALK || owner->curInt->curAction->actionType == EActionType::SHOOT) //hide when stack walks or shoots
|
||||
return false;
|
||||
|
||||
else if(owner->curInt->curAction->actionType == EActionType::WALK_AND_ATTACK && currentActionTarget != stack->getPosition()) //when attacking, hide until walk phase finished
|
||||
return false;
|
||||
}
|
||||
|
||||
if(owner->curInt->curAction)
|
||||
{
|
||||
if(owner->curInt->curAction->stackNumber == stack->ID) //stack is currently taking action (is not a target of another creature's action etc)
|
||||
{
|
||||
if(owner->curInt->curAction->actionType == EActionType::WALK || owner->curInt->curAction->actionType == EActionType::SHOOT) //hide when stack walks or shoots
|
||||
return false;
|
||||
|
||||
else if(owner->curInt->curAction->actionType == EActionType::WALK_AND_ATTACK && currentActionTarget != stack->getPosition()) //when attacking, hide until walk phase finished
|
||||
return false;
|
||||
}
|
||||
|
||||
if(owner->curInt->curAction->actionType == EActionType::SHOOT && currentActionTarget == stack->getPosition()) //hide if we are ranged attack target
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
if(owner->curInt->curAction->actionType == EActionType::SHOOT && currentActionTarget == stack->getPosition()) //hide if we are ranged attack target
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBattleStacksController::showStackAmountBox(SDL_Surface *to, const CStack * stack)
|
||||
{
|
||||
auto getEffectsPositivness = [&](const std::vector<si32> & activeSpells) -> int
|
||||
{
|
||||
int pos = 0;
|
||||
@ -316,7 +299,7 @@ void CBattleStacksController::showAliveStacks(SDL_Surface *to, std::vector<const
|
||||
return pos;
|
||||
};
|
||||
|
||||
auto getAmountBoxBackground = [&](int positivness) -> SDL_Surface *
|
||||
auto getAmountBoxBackground = [&](int positivness) -> auto
|
||||
{
|
||||
if (positivness > 0)
|
||||
return amountPositive;
|
||||
@ -325,48 +308,36 @@ void CBattleStacksController::showAliveStacks(SDL_Surface *to, std::vector<const
|
||||
return amountEffNeutral;
|
||||
};
|
||||
|
||||
showStacks(to, stacks); // Actual display of all stacks
|
||||
const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1;
|
||||
const int reverseSideShift = stack->side == BattleSide::ATTACKER ? -1 : 1;
|
||||
const BattleHex nextPos = stack->getPosition() + sideShift;
|
||||
const bool edge = stack->getPosition() % GameConstants::BFIELD_WIDTH == (stack->side == BattleSide::ATTACKER ? GameConstants::BFIELD_WIDTH - 2 : 1);
|
||||
const bool moveInside = !edge && !owner->fieldController->stackCountOutsideHex(nextPos);
|
||||
int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) +
|
||||
(stack->doubleWide() ? 44 : 0) * sideShift +
|
||||
(moveInside ? amountNormal->width() + 10 : 0) * reverseSideShift;
|
||||
int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15);
|
||||
|
||||
for (auto & stack : stacks)
|
||||
{
|
||||
assert(stack);
|
||||
//printing amount
|
||||
if (isAmountBoxVisible(stack))
|
||||
{
|
||||
const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1;
|
||||
const int reverseSideShift = stack->side == BattleSide::ATTACKER ? -1 : 1;
|
||||
const BattleHex nextPos = stack->getPosition() + sideShift;
|
||||
const bool edge = stack->getPosition() % GameConstants::BFIELD_WIDTH == (stack->side == BattleSide::ATTACKER ? GameConstants::BFIELD_WIDTH - 2 : 1);
|
||||
const bool moveInside = !edge && !owner->fieldController->stackCountOutsideHex(nextPos);
|
||||
int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) +
|
||||
(stack->doubleWide() ? 44 : 0) * sideShift +
|
||||
(moveInside ? amountNormal->w + 10 : 0) * reverseSideShift;
|
||||
int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15);
|
||||
//blitting amount background box
|
||||
std::vector<si32> activeSpells = stack->activeSpells();
|
||||
|
||||
//blitting amount background box
|
||||
SDL_Surface *amountBG = amountNormal;
|
||||
std::vector<si32> activeSpells = stack->activeSpells();
|
||||
if (!activeSpells.empty())
|
||||
amountBG = getAmountBoxBackground(getEffectsPositivness(activeSpells));
|
||||
auto amountBG = activeSpells.empty() ? amountNormal : getAmountBoxBackground(getEffectsPositivness(activeSpells));
|
||||
amountBG->draw(to, creAnims[stack->ID]->pos.x + xAdd, creAnims[stack->ID]->pos.y + yAdd);
|
||||
|
||||
SDL_Rect temp_rect = genRect(amountBG->h, amountBG->w, creAnims[stack->ID]->pos.x + xAdd, creAnims[stack->ID]->pos.y + yAdd);
|
||||
SDL_BlitSurface(amountBG, nullptr, to, &temp_rect);
|
||||
//blitting amount
|
||||
Point textPos(creAnims[stack->ID]->pos.x + xAdd + amountNormal->width()/2,
|
||||
creAnims[stack->ID]->pos.y + yAdd + amountNormal->height()/2);
|
||||
graphics->fonts[FONT_TINY]->renderTextCenter(to, makeNumberShort(stack->getCount()), Colors::WHITE, textPos);
|
||||
|
||||
//blitting amount
|
||||
Point textPos(creAnims[stack->ID]->pos.x + xAdd + amountNormal->w/2,
|
||||
creAnims[stack->ID]->pos.y + yAdd + amountNormal->h/2);
|
||||
graphics->fonts[FONT_TINY]->renderTextCenter(to, makeNumberShort(stack->getCount()), Colors::WHITE, textPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleStacksController::showStacks(SDL_Surface *to, std::vector<const CStack *> stacks)
|
||||
void CBattleStacksController::showStack(SDL_Surface *to, const CStack * stack)
|
||||
{
|
||||
for (const CStack *stack : stacks)
|
||||
{
|
||||
creAnims[stack->ID]->nextFrame(to, facingRight(stack)); // do actual blit
|
||||
creAnims[stack->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
}
|
||||
creAnims[stack->ID]->nextFrame(to, facingRight(stack)); // do actual blit
|
||||
creAnims[stack->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
|
||||
if (stackNeedsAmountBox(stack))
|
||||
showStackAmountBox(to, stack);
|
||||
}
|
||||
|
||||
void CBattleStacksController::updateBattleAnimations()
|
||||
|
@ -21,15 +21,16 @@ class CBattleAnimation;
|
||||
class CCreatureAnimation;
|
||||
class CStack;
|
||||
class CBattleAnimation;
|
||||
class IImage;
|
||||
|
||||
class CBattleStacksController
|
||||
{
|
||||
CBattleInterface * owner;
|
||||
|
||||
SDL_Surface *amountNormal;
|
||||
SDL_Surface *amountNegative;
|
||||
SDL_Surface *amountPositive;
|
||||
SDL_Surface *amountEffNeutral;
|
||||
std::shared_ptr<IImage> amountNormal;
|
||||
std::shared_ptr<IImage> amountNegative;
|
||||
std::shared_ptr<IImage> amountPositive;
|
||||
std::shared_ptr<IImage> amountEffNeutral;
|
||||
|
||||
std::list<std::pair<CBattleAnimation *, bool>> pendingAnims; //currently displayed animations <anim, initialized>
|
||||
std::map<int32_t, std::shared_ptr<CCreatureAnimation>> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
|
||||
@ -44,11 +45,13 @@ class CBattleStacksController
|
||||
si32 creatureSpellToCast;
|
||||
|
||||
ui32 animIDhelper; //for giving IDs for animations
|
||||
|
||||
bool stackNeedsAmountBox(const CStack * stack);
|
||||
void showStackAmountBox(SDL_Surface *to, const CStack * stack);
|
||||
|
||||
public:
|
||||
CBattleStacksController(CBattleInterface * owner);
|
||||
~CBattleStacksController();
|
||||
|
||||
void sortObjectsByHex(BattleObjectsByHex & sorted);
|
||||
bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex);
|
||||
bool facingRight(const CStack * stack);
|
||||
|
||||
@ -72,8 +75,10 @@ public:
|
||||
void setHoveredStack(const CStack *stack);
|
||||
void setSelectedStack(const CStack *stack);
|
||||
|
||||
void showAliveStacks(SDL_Surface *to, std::vector<const CStack *> stacks);
|
||||
void showStacks(SDL_Surface *to, std::vector<const CStack *> stacks);
|
||||
void showAliveStack(SDL_Surface *to, const CStack * stack);
|
||||
void showStack(SDL_Surface *to, const CStack * stack);
|
||||
|
||||
void showBattlefieldObjects(SDL_Surface *to, const BattleHex & location );
|
||||
|
||||
void addNewAnim(CBattleAnimation *anim); //adds new anim to pendingAnims
|
||||
void updateBattleAnimations();
|
||||
|
@ -134,6 +134,11 @@ public:
|
||||
~SDLImageLoader();
|
||||
};
|
||||
|
||||
std::shared_ptr<IImage> IImage::createFromFile( const std::string & path )
|
||||
{
|
||||
return std::shared_ptr<IImage>(new SDLImage(path));
|
||||
}
|
||||
|
||||
// Extremely simple file cache. TODO: smarter, more general solution
|
||||
class CFileCache
|
||||
{
|
||||
|
@ -69,6 +69,9 @@ public:
|
||||
|
||||
IImage();
|
||||
virtual ~IImage();
|
||||
|
||||
/// loads image from specified file. Returns 0-sized images on failure
|
||||
static std::shared_ptr<IImage> createFromFile( const std::string & path );
|
||||
};
|
||||
|
||||
/// Class for handling animation
|
||||
|
@ -155,37 +155,50 @@ typedef void (*BlitterWithRotationVal)(SDL_Surface *src,SDL_Rect srcRect, SDL_Su
|
||||
class ColorShifter
|
||||
{
|
||||
public:
|
||||
virtual ~ColorShifter() = default;
|
||||
virtual SDL_Color shiftColor(SDL_Color clr) const = 0;
|
||||
~ColorShifter() = default;
|
||||
virtual SDL_Color shiftColor(SDL_Color input) const = 0;
|
||||
};
|
||||
|
||||
class ColorShifterLightBlue : public ColorShifter
|
||||
class ColorShifterAddMul : public ColorShifter
|
||||
{
|
||||
SDL_Color add;
|
||||
SDL_Color mul;
|
||||
public:
|
||||
SDL_Color shiftColor(SDL_Color clr) const override
|
||||
|
||||
static ColorShifterAddMul deepBlue()
|
||||
{
|
||||
clr.b = clr.b + (255 - clr.b) / 2;
|
||||
return clr;
|
||||
return ColorShifterAddMul({0, 0, 255, 0}, {255, 255, 0, 255});
|
||||
}
|
||||
|
||||
ColorShifterAddMul(SDL_Color add, SDL_Color mul) :
|
||||
add(add),
|
||||
mul(mul)
|
||||
{}
|
||||
|
||||
SDL_Color shiftColor(SDL_Color input) const override
|
||||
{
|
||||
return {
|
||||
uint8_t(std::min(255.f, std::round(input.r * float(mul.r) / 255 + add.r))),
|
||||
uint8_t(std::min(255.f, std::round(input.g * float(mul.g) / 255 + add.g))),
|
||||
uint8_t(std::min(255.f, std::round(input.b * float(mul.b) / 255 + add.b))),
|
||||
uint8_t(std::min(255.f, std::round(input.a * float(mul.a) / 255 + add.a))),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class ColorShifterDeepBlue : public ColorShifter
|
||||
class ColorShifterAddMulExcept : public ColorShifterAddMul
|
||||
{
|
||||
SDL_Color ignored;
|
||||
public:
|
||||
SDL_Color shiftColor(SDL_Color clr) const override
|
||||
{
|
||||
clr.b = 255;
|
||||
return clr;
|
||||
}
|
||||
};
|
||||
ColorShifterAddMulExcept(SDL_Color add, SDL_Color mul, SDL_Color ignored) :
|
||||
ColorShifterAddMul(add, mul)
|
||||
{}
|
||||
|
||||
class ColorShifterDeepRed : public ColorShifter
|
||||
{
|
||||
public:
|
||||
SDL_Color shiftColor(SDL_Color clr) const override
|
||||
SDL_Color shiftColor(SDL_Color input) const override
|
||||
{
|
||||
clr.r = 255;
|
||||
return clr;
|
||||
if ( input.r == ignored.r && input.g == ignored.g && input.b == ignored.b && input.a == ignored.a)
|
||||
return input;
|
||||
return ColorShifterAddMul::shiftColor(input);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -468,18 +468,18 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
|
||||
else
|
||||
whoCanPlay = HUMAN;
|
||||
|
||||
static const char * flags[] =
|
||||
static std::string flags[] =
|
||||
{
|
||||
"AOFLGBR.DEF", "AOFLGBB.DEF", "AOFLGBY.DEF", "AOFLGBG.DEF",
|
||||
"AOFLGBO.DEF", "AOFLGBP.DEF", "AOFLGBT.DEF", "AOFLGBS.DEF"
|
||||
};
|
||||
static const char * bgs[] =
|
||||
static std::string bgs[] =
|
||||
{
|
||||
"ADOPRPNL.bmp", "ADOPBPNL.bmp", "ADOPYPNL.bmp", "ADOPGPNL.bmp",
|
||||
"ADOPOPNL.bmp", "ADOPPPNL.bmp", "ADOPTPNL.bmp", "ADOPSPNL.bmp"
|
||||
};
|
||||
|
||||
background = std::make_shared<CPicture>(BitmapHandler::loadBitmap(bgs[s.color.getNum()]), 0, 0, true);
|
||||
background = std::make_shared<CPicture>(bgs[s.color.getNum()], 0, 0);
|
||||
labelPlayerName = std::make_shared<CLabel>(55, 10, EFonts::FONT_SMALL, EAlignment::CENTER, Colors::WHITE, s.name);
|
||||
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
|
||||
|
||||
|
@ -34,10 +34,15 @@ typedef boost::optional<ui8> BattleSideOpt;
|
||||
// for battle stacks' positions
|
||||
struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
|
||||
{
|
||||
// helpers for siege
|
||||
static const si16 CASTLE_CENTRAL_TOWER = -2;
|
||||
static const si16 CASTLE_BOTTOM_TOWER = -3;
|
||||
static const si16 CASTLE_UPPER_TOWER = -4;
|
||||
|
||||
// helpers for rendering
|
||||
static const si16 HEX_BEFORE_ALL = std::numeric_limits<si16>::min();
|
||||
static const si16 HEX_AFTER_ALL = std::numeric_limits<si16>::max();
|
||||
|
||||
si16 hex;
|
||||
static const si16 INVALID = -1;
|
||||
enum EDir
|
||||
|
Loading…
x
Reference in New Issue
Block a user