mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Made speed of most of battle animations time-based
- speed of creature-related animation no longer depends on framerate - most of values from cranim.txt are now used correctly - removed BMPPalette struct in favor of SDL_Color - animation group "DEAD" that is used to display dead stacks, by default consists from last frame of "DEATH" group Notes: - animation speed may still need some adjustments - range of config property "battle/animationSpeed" has been changed. This may result in extremely fast animation. To fix - change animation speed via options.
This commit is contained in:
parent
68603245c4
commit
146a5e5ef8
@ -320,15 +320,14 @@ public:
|
||||
|
||||
enum EAnimType // list of creature animations, numbers were taken from def files
|
||||
{
|
||||
WHOLE_ANIM=-1, //just for convenience
|
||||
MOVING=0, //will automatically add MOVE_START and MOVE_END to queue
|
||||
MOUSEON=1, //rename to IDLE
|
||||
HOLDING=2, //rename to STANDING
|
||||
MOVING=0,
|
||||
MOUSEON=1,
|
||||
HOLDING=2,
|
||||
HITTED=3,
|
||||
DEFENCE=4,
|
||||
DEATH=5,
|
||||
//DEATH2=6, //unused?
|
||||
TURN_L=7, //will automatically play second part of anim and rotate creature
|
||||
TURN_L=7,
|
||||
TURN_R=8, //same
|
||||
//TURN_L2=9, //identical to previous?
|
||||
//TURN_R2=10,
|
||||
@ -341,13 +340,10 @@ public:
|
||||
CAST_UP=17,
|
||||
CAST_FRONT=18,
|
||||
CAST_DOWN=19,
|
||||
DHEX_ATTACK_UP=17,
|
||||
DHEX_ATTACK_FRONT=18,
|
||||
DHEX_ATTACK_DOWN=19,
|
||||
MOVE_START=20, //no need to use this two directly - MOVING will be enought
|
||||
MOVE_END=21
|
||||
//MOUSEON=22 //special group for border-only images - IDLE will be used as base
|
||||
//READY=23 //same but STANDING is base
|
||||
MOVE_START=20,
|
||||
MOVE_END=21,
|
||||
DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
struct SDL_Surface;
|
||||
|
||||
/*
|
||||
* CBitmapHandler.h, part of VCMI engine
|
||||
*
|
||||
@ -14,11 +10,7 @@ struct SDL_Surface;
|
||||
*
|
||||
*/
|
||||
|
||||
/// Struct which stands for a simple rgba palette
|
||||
struct BMPPalette
|
||||
{
|
||||
ui8 R,G,B,F;
|
||||
};
|
||||
struct SDL_Surface;
|
||||
|
||||
namespace BitmapHandler
|
||||
{
|
||||
|
@ -106,8 +106,8 @@ CCreatureWindow::CCreatureWindow(const CStackInstance &st, CreWinType Type, std:
|
||||
fs += Upg;
|
||||
fs += boost::bind(&CCreatureWindow::close,this);
|
||||
CFunctionList<void()> cfl;
|
||||
cfl = [&] {
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[207], fs, nullptr, false, upgResCost); };
|
||||
cfl += boost::bind(&CPlayerInterface::showYesNoDialog,
|
||||
LOCPLINT, CGI->generaltexth->allTexts[207], fs, nullptr, false, upgResCost);
|
||||
upgrade = new CAdventureMapButton("",CGI->generaltexth->zelp[446].second,cfl,385, 148,"IVIEWCR.DEF",SDLK_u);
|
||||
}
|
||||
else
|
||||
|
@ -52,7 +52,7 @@ CDefEssential::~CDefEssential()
|
||||
|
||||
void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
||||
{
|
||||
BMPPalette palette[256];
|
||||
SDL_Color palette[256];
|
||||
SDefEntry &de = * reinterpret_cast<SDefEntry *>(table);
|
||||
ui8 *p;
|
||||
|
||||
@ -64,10 +64,10 @@ void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
||||
|
||||
for (ui32 it=0;it<256;it++)
|
||||
{
|
||||
palette[it].R = de.palette[it].R;
|
||||
palette[it].G = de.palette[it].G;
|
||||
palette[it].B = de.palette[it].B;
|
||||
palette[it].F = 255;
|
||||
palette[it].r = de.palette[it].R;
|
||||
palette[it].g = de.palette[it].G;
|
||||
palette[it].b = de.palette[it].B;
|
||||
palette[it].unused = 255;
|
||||
}
|
||||
|
||||
// The SDefEntryBlock starts just after the SDefEntry
|
||||
@ -128,7 +128,7 @@ void CDefHandler::expand(ui8 N,ui8 & BL, ui8 & BR)
|
||||
BR = N & 0x1F;
|
||||
}
|
||||
|
||||
SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const BMPPalette * palette) const
|
||||
SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const
|
||||
{
|
||||
SDL_Surface * ret=nullptr;
|
||||
|
||||
@ -176,10 +176,10 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const BMPPal
|
||||
for(int i=0; i<256; ++i)
|
||||
{
|
||||
SDL_Color pr;
|
||||
pr.r = palette[i].R;
|
||||
pr.g = palette[i].G;
|
||||
pr.b = palette[i].B;
|
||||
pr.unused = palette[i].F;
|
||||
pr.r = palette[i].r;
|
||||
pr.g = palette[i].g;
|
||||
pr.b = palette[i].b;
|
||||
pr.unused = palette[i].unused;
|
||||
(*(ret->format->palette->colors+i))=pr;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "../lib/vcmi_endian.h"
|
||||
|
||||
struct SDL_Surface;
|
||||
struct BMPPalette;
|
||||
struct SDL_Color;
|
||||
|
||||
/*
|
||||
* CDefHandler.h, part of VCMI engine
|
||||
@ -94,7 +94,7 @@ public:
|
||||
|
||||
CDefHandler(); //c-tor
|
||||
~CDefHandler(); //d-tor
|
||||
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const BMPPalette * palette) const; //saves picture with given number to "testtt.bmp"
|
||||
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const; //saves picture with given number to "testtt.bmp"
|
||||
static void expand(ui8 N,ui8 & BL, ui8 & BR);
|
||||
void openFromMemory(ui8 * table, const std::string & name);
|
||||
CDefEssential * essentialize();
|
||||
|
@ -638,7 +638,7 @@ void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, u
|
||||
for(auto & healedStack : healedStacks)
|
||||
{
|
||||
const CStack * healed = cb->battleGetStackByID(healedStack.first);
|
||||
if(battleInt->creAnims[healed->ID]->getType() == CCreatureAnim::DEATH)
|
||||
if(battleInt->creAnims[healed->ID]->isDead())
|
||||
{
|
||||
//stack has been resurrected
|
||||
battleInt->creAnims[healed->ID]->setType(CCreatureAnim::HOLDING);
|
||||
|
@ -27,11 +27,8 @@ public:
|
||||
}
|
||||
CFunctionList(const boost::function<Signature> &first)
|
||||
{
|
||||
funcs.push_back(first);
|
||||
}
|
||||
CFunctionList(boost::function<Signature> &first)
|
||||
{
|
||||
funcs.push_back(first);
|
||||
if (first)
|
||||
funcs.push_back(first);
|
||||
}
|
||||
CFunctionList(std::nullptr_t)
|
||||
{}
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "gui/SDL_Extensions.h"
|
||||
|
||||
#include "CAdvmapInterface.h"
|
||||
#include "CBitmapHandler.h"
|
||||
#include "CDefHandler.h"
|
||||
#include "battle/CBattleInterface.h"
|
||||
#include "battle/CBattleInterfaceClasses.h"
|
||||
#include "../CCallback.h"
|
||||
|
@ -2,22 +2,26 @@
|
||||
#include "CBattleAnimations.h"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "CBattleInterface.h"
|
||||
|
||||
#include "CBattleInterfaceClasses.h"
|
||||
#include "CBattleInterface.h"
|
||||
#include "CCreatureAnimation.h"
|
||||
#include "../../lib/BattleState.h"
|
||||
|
||||
#include "../CDefHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../../CCallback.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/BattleState.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
|
||||
|
||||
CBattleAnimation::CBattleAnimation(CBattleInterface * _owner)
|
||||
: owner(_owner), ID(_owner->animIDhelper++)
|
||||
: owner(_owner), ID(_owner->animIDhelper++)
|
||||
{}
|
||||
|
||||
void CBattleAnimation::endAnim()
|
||||
@ -29,7 +33,6 @@ void CBattleAnimation::endAnim()
|
||||
elem.first = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CBattleAnimation::isEarliest(bool perStackConcurrency)
|
||||
@ -59,33 +62,35 @@ bool CBattleAnimation::isEarliest(bool perStackConcurrency)
|
||||
return (ID == lowestMoveID) || (lowestMoveID == (owner->animIDhelper + 5));
|
||||
}
|
||||
|
||||
CBattleStackAnimation::CBattleStackAnimation(CBattleInterface * _owner, const CStack * _stack)
|
||||
: CBattleAnimation(_owner), stack(_stack)
|
||||
CBattleStackAnimation::CBattleStackAnimation(CBattleInterface * owner, const CStack * stack)
|
||||
: CBattleAnimation(owner),
|
||||
myAnim(owner->creAnims[stack->ID]),
|
||||
stack(stack)
|
||||
{}
|
||||
|
||||
CCreatureAnimation* CBattleStackAnimation::myAnim()
|
||||
{
|
||||
return owner->creAnims[stack->ID];
|
||||
}
|
||||
|
||||
void CAttackAnimation::nextFrame()
|
||||
{
|
||||
if(myAnim()->getType() != group)
|
||||
myAnim()->setType(group);
|
||||
if(myAnim->getType() != group)
|
||||
{
|
||||
myAnim->setType(group);
|
||||
myAnim->onAnimationReset += std::bind(&CAttackAnimation::endAnim, this);
|
||||
}
|
||||
|
||||
if(myAnim()->onFirstFrameInGroup())
|
||||
if(!soundPlayed)
|
||||
{
|
||||
if(shooting)
|
||||
CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), shoot));
|
||||
else
|
||||
CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), attack));
|
||||
soundPlayed = true;
|
||||
}
|
||||
else if(myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
myAnim()->setType(CCreatureAnim::HOLDING);
|
||||
endAnim();
|
||||
return; //execution of endAnim deletes this !!!
|
||||
}
|
||||
CBattleAnimation::nextFrame();
|
||||
}
|
||||
|
||||
void CAttackAnimation::endAnim()
|
||||
{
|
||||
myAnim->setType(CCreatureAnim::HOLDING);
|
||||
CBattleStackAnimation::endAnim();
|
||||
}
|
||||
|
||||
bool CAttackAnimation::checkInitialConditions()
|
||||
@ -94,7 +99,9 @@ bool CAttackAnimation::checkInitialConditions()
|
||||
}
|
||||
|
||||
CAttackAnimation::CAttackAnimation(CBattleInterface *_owner, const CStack *attacker, BattleHex _dest, const CStack *defender)
|
||||
: CBattleStackAnimation(_owner, attacker), dest(_dest), attackedStack(defender), attackingStack(attacker)
|
||||
: CBattleStackAnimation(_owner, attacker),
|
||||
soundPlayed(false),
|
||||
dest(_dest), attackedStack(defender), attackingStack(attacker)
|
||||
{
|
||||
|
||||
assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
|
||||
@ -113,13 +120,6 @@ killed(_attackedInfo.killed)
|
||||
|
||||
bool CDefenceAnimation::init()
|
||||
{
|
||||
//checking initial conditions
|
||||
|
||||
//if(owner->creAnims[stackID]->getType() != 2)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if(attacker == nullptr && owner->battleEffects.size() > 0)
|
||||
return false;
|
||||
|
||||
@ -154,8 +154,6 @@ bool CDefenceAnimation::init()
|
||||
if(ID > lowestMoveID)
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
//reverse unit if necessary
|
||||
if (attacker && owner->curInt->cb->isToReverse(stack->position, attacker->position, owner->creDir[stack->ID], attacker->doubleWide(), owner->creDir[attacker->ID]))
|
||||
{
|
||||
@ -179,54 +177,33 @@ bool CDefenceAnimation::init()
|
||||
if(killed)
|
||||
{
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), killed));
|
||||
myAnim()->setType(CCreatureAnim::DEATH); //death
|
||||
myAnim->setType(CCreatureAnim::DEATH); //death
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: this block doesn't seems correct if the unit is defending.
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), wince));
|
||||
myAnim()->setType(CCreatureAnim::HITTED); //getting hit
|
||||
myAnim->setType(CCreatureAnim::HITTED); //getting hit
|
||||
}
|
||||
|
||||
myAnim->onAnimationReset += std::bind(&CDefenceAnimation::endAnim, this);
|
||||
|
||||
return true; //initialized successfuly
|
||||
}
|
||||
|
||||
void CDefenceAnimation::nextFrame()
|
||||
{
|
||||
if(!killed && myAnim()->getType() != CCreatureAnim::HITTED)
|
||||
{
|
||||
myAnim()->setType(CCreatureAnim::HITTED);
|
||||
}
|
||||
|
||||
if(!myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
if( myAnim()->getType() == CCreatureAnim::DEATH && (owner->animCount+1)%(4/owner->getAnimSpeed())==0
|
||||
&& !myAnim()->onLastFrameInGroup() )
|
||||
{
|
||||
myAnim()->incrementFrame();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endAnim();
|
||||
}
|
||||
assert(myAnim->getType() == CCreatureAnim::HITTED || myAnim->getType() == CCreatureAnim::DEATH);
|
||||
|
||||
CBattleAnimation::nextFrame();
|
||||
}
|
||||
|
||||
void CDefenceAnimation::endAnim()
|
||||
{
|
||||
//restoring animType
|
||||
|
||||
if(myAnim()->getType() == CCreatureAnim::HITTED)
|
||||
myAnim()->setType(CCreatureAnim::HOLDING);
|
||||
|
||||
//printing info to console
|
||||
|
||||
//if(attacker!=nullptr)
|
||||
// owner->printConsoleAttacked(stack, dmg, amountKilled, attacker);
|
||||
|
||||
//const CStack * attacker = owner->curInt->cb->battleGetStackByID(IDby, false);
|
||||
//const CStack * attacked = owner->curInt->cb->battleGetStackByID(stackID, false);
|
||||
if (killed)
|
||||
myAnim->setType(CCreatureAnim::DEAD);
|
||||
else
|
||||
myAnim->setType(CCreatureAnim::HOLDING);
|
||||
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
@ -234,7 +211,7 @@ void CDefenceAnimation::endAnim()
|
||||
}
|
||||
|
||||
CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames)
|
||||
: CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
|
||||
: CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
|
||||
{}
|
||||
|
||||
bool CDummyAnimation::init()
|
||||
@ -261,12 +238,7 @@ bool CMeleeAttackAnimation::init()
|
||||
if( !CAttackAnimation::checkInitialConditions() )
|
||||
return false;
|
||||
|
||||
//if(owner->creAnims[stackID]->getType()!=2)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if(!attackingStack || myAnim()->getType() == 5)
|
||||
if(!attackingStack || myAnim->isDead())
|
||||
{
|
||||
endAnim();
|
||||
|
||||
@ -277,7 +249,6 @@ bool CMeleeAttackAnimation::init()
|
||||
|
||||
if (toReverse)
|
||||
{
|
||||
|
||||
owner->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn, true));
|
||||
return false;
|
||||
}
|
||||
@ -323,155 +294,124 @@ CMeleeAttackAnimation::CMeleeAttackAnimation(CBattleInterface * _owner, const CS
|
||||
: CAttackAnimation(_owner, attacker, _dest, _attacked)
|
||||
{}
|
||||
|
||||
void CMeleeAttackAnimation::nextFrame()
|
||||
{
|
||||
/*for(std::list<std::pair<CBattleAnimation *, bool> >::const_iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
|
||||
{
|
||||
CBattleMoveStart * anim = dynamic_cast<CBattleMoveStart *>(it->first);
|
||||
CReverseAnim * anim2 = dynamic_cast<CReverseAnim *>(it->first);
|
||||
if( (anim && anim->stackID == stackID) || (anim2 && anim2->stackID == stackID ) )
|
||||
return;
|
||||
}*/
|
||||
|
||||
CAttackAnimation::nextFrame();
|
||||
}
|
||||
|
||||
void CMeleeAttackAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
CAttackAnimation::endAnim();
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CMovementAnimation::shouldRotate()
|
||||
{
|
||||
Point begPosition = CClickableHex::getXYUnitAnim(oldPos, stack->attackerOwned, stack, owner);
|
||||
Point endPosition = CClickableHex::getXYUnitAnim(nextHex, stack->attackerOwned, stack, owner);
|
||||
|
||||
if((begPosition.x > endPosition.x) && owner->creDir[stack->ID] == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((begPosition.x < endPosition.x) && owner->creDir[stack->ID] == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMovementAnimation::init()
|
||||
{
|
||||
if( !isEarliest(false) )
|
||||
if( !isEarliest(false) )
|
||||
return false;
|
||||
|
||||
//a few useful variables
|
||||
steps = static_cast<int>(myAnim()->framesInGroup(CCreatureAnim::MOVING) * owner->getAnimSpeedMultiplier() - 1);
|
||||
|
||||
const CStack * movedStack = stack;
|
||||
if(!movedStack || myAnim()->getType() == 5)
|
||||
if(!stack || myAnim->isDead())
|
||||
{
|
||||
endAnim();
|
||||
return false;
|
||||
}
|
||||
|
||||
Point begPosition = CClickableHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack, owner);
|
||||
Point endPosition = CClickableHex::getXYUnitAnim(nextHex, movedStack->attackerOwned, movedStack, owner);
|
||||
|
||||
if(steps < 0 || stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1))) //no movement or teleport
|
||||
if(owner->creAnims[stack->ID]->framesInGroup(CCreatureAnim::MOVING) == 0 ||
|
||||
stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1)))
|
||||
{
|
||||
//this creature seems to have no move animation so we can end it immediately
|
||||
//no movement or teleport, end immediately
|
||||
endAnim();
|
||||
return false;
|
||||
}
|
||||
whichStep = 0;
|
||||
int hexWbase = 44, hexHbase = 42;
|
||||
|
||||
//bool twoTiles = movedStack->doubleWide();
|
||||
|
||||
int mutPos = BattleHex::mutualPosition(curStackPos, nextHex);
|
||||
|
||||
//reverse unit if necessary
|
||||
if((begPosition.x > endPosition.x) && owner->creDir[stack->ID] == true)
|
||||
if(shouldRotate())
|
||||
{
|
||||
owner->addNewAnim(new CReverseAnimation(owner, stack, curStackPos, true));
|
||||
return false;
|
||||
}
|
||||
else if ((begPosition.x < endPosition.x) && owner->creDir[stack->ID] == false)
|
||||
{
|
||||
owner->addNewAnim(new CReverseAnimation(owner, stack, curStackPos, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(myAnim()->getType() != CCreatureAnim::MOVING)
|
||||
{
|
||||
myAnim()->setType(CCreatureAnim::MOVING);
|
||||
}
|
||||
//unit reversed
|
||||
|
||||
if (owner->moveSh == -1)
|
||||
{
|
||||
owner->moveSh = CCS->soundh->playSound(battle_sound(movedStack->getCreature(), move), -1);
|
||||
}
|
||||
|
||||
//step shift calculation
|
||||
posX = myAnim()->pos.x, posY = myAnim()->pos.y; // for precise calculations ;]
|
||||
if(mutPos == -1 && movedStack->hasBonusOfType(Bonus::FLYING))
|
||||
{
|
||||
steps *= distance;
|
||||
steps /= 2; //to make animation faster
|
||||
|
||||
stepX = (endPosition.x - begPosition.x) / static_cast<double>(steps);
|
||||
stepY = (endPosition.y - begPosition.y) / static_cast<double>(steps);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(mutPos)
|
||||
// it seems that H3 does NOT plays full rotation animation here in most situations
|
||||
// Logical since it takes quite a lot of time
|
||||
if (curentMoveIndex == 0) // full rotation only for moving towards first tile.
|
||||
{
|
||||
case 0:
|
||||
stepX = -1.0 * (hexWbase / (2.0 * steps));
|
||||
stepY = -1.0 * (hexHbase / (static_cast<double>(steps)));
|
||||
break;
|
||||
case 1:
|
||||
stepX = hexWbase / (2.0 * steps);
|
||||
stepY = -1.0 * hexHbase / (static_cast<double>(steps));
|
||||
break;
|
||||
case 2:
|
||||
stepX = hexWbase / static_cast<double>(steps);
|
||||
stepY = 0.0;
|
||||
break;
|
||||
case 3:
|
||||
stepX = hexWbase / (2.0 * steps);
|
||||
stepY = hexHbase / static_cast<double>(steps);
|
||||
break;
|
||||
case 4:
|
||||
stepX = -1.0 * hexWbase / (2.0 * steps);
|
||||
stepY = hexHbase / static_cast<double>(steps);
|
||||
break;
|
||||
case 5:
|
||||
stepX = -1.0 * hexWbase / static_cast<double>(steps);
|
||||
stepY = 0.0;
|
||||
break;
|
||||
owner->addNewAnim(new CReverseAnimation(owner, stack, oldPos, true));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CReverseAnimation::rotateStack(owner, stack, oldPos);
|
||||
}
|
||||
}
|
||||
//step shifts calculated
|
||||
|
||||
if(myAnim->getType() != CCreatureAnim::MOVING)
|
||||
{
|
||||
myAnim->setType(CCreatureAnim::MOVING);
|
||||
}
|
||||
|
||||
if (owner->moveSoundHander == -1)
|
||||
{
|
||||
owner->moveSoundHander = CCS->soundh->playSound(battle_sound(stack->getCreature(), move), -1);
|
||||
}
|
||||
|
||||
Point begPosition = CClickableHex::getXYUnitAnim(oldPos, stack->attackerOwned, stack, owner);
|
||||
Point endPosition = CClickableHex::getXYUnitAnim(nextHex, stack->attackerOwned, stack, owner);
|
||||
|
||||
timeToMove = AnimationControls::getMovementDuration(stack->getCreature());
|
||||
|
||||
begX = begPosition.x;
|
||||
begY = begPosition.y;
|
||||
progress = 0;
|
||||
distanceX = endPosition.x - begPosition.x;
|
||||
distanceY = endPosition.y - begPosition.y;
|
||||
|
||||
if (stack->hasBonus(Selector::type(Bonus::FLYING)))
|
||||
{
|
||||
float distance = sqrt(distanceX * distanceX + distanceY * distanceY);
|
||||
|
||||
timeToMove *= distance / AnimationControls::getFlightDistance(stack->getCreature());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMovementAnimation::nextFrame()
|
||||
{
|
||||
//moving instructions
|
||||
posX += stepX;
|
||||
myAnim()->pos.x = static_cast<Sint16>(posX);
|
||||
posY += stepY;
|
||||
myAnim()->pos.y = static_cast<Sint16>(posY);
|
||||
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * timeToMove;
|
||||
|
||||
// Increments step count and check if we are finished with current animation
|
||||
++whichStep;
|
||||
if(whichStep == steps)
|
||||
//moving instructions
|
||||
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
|
||||
myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress );
|
||||
|
||||
CBattleAnimation::nextFrame();
|
||||
|
||||
if(progress >= 1.0)
|
||||
{
|
||||
// Sets the position of the creature animation sprites
|
||||
Point coords = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], stack, owner);
|
||||
myAnim()->pos = coords;
|
||||
myAnim->pos = coords;
|
||||
|
||||
// true if creature haven't reached the final destination hex
|
||||
if ((nextPos + 1) < destTiles.size())
|
||||
if ((curentMoveIndex + 1) < destTiles.size())
|
||||
{
|
||||
// update the next hex field which has to be reached by the stack
|
||||
nextPos++;
|
||||
curStackPos = nextHex;
|
||||
nextHex = destTiles[nextPos];
|
||||
curentMoveIndex++;
|
||||
oldPos = nextHex;
|
||||
nextHex = destTiles[curentMoveIndex];
|
||||
|
||||
// update position of double wide creatures
|
||||
bool twoTiles = stack->doubleWide();
|
||||
if(twoTiles && bool(stack->attackerOwned) && (owner->creDir[stack->ID] != bool(stack->attackerOwned) )) //big attacker creature is reversed
|
||||
myAnim()->pos.x -= 44;
|
||||
myAnim->pos.x -= 44;
|
||||
else if(twoTiles && (! bool(stack->attackerOwned) ) && (owner->creDir[stack->ID] != bool(stack->attackerOwned) )) //big defender creature is reversed
|
||||
myAnim()->pos.x += 44;
|
||||
myAnim->pos.x += 44;
|
||||
|
||||
// re-init animation
|
||||
for(auto & elem : owner->pendingAnims)
|
||||
@ -490,27 +430,32 @@ void CMovementAnimation::nextFrame()
|
||||
|
||||
void CMovementAnimation::endAnim()
|
||||
{
|
||||
const CStack * movedStack = stack;
|
||||
assert(stack);
|
||||
|
||||
myAnim()->pos = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], movedStack, owner);
|
||||
myAnim->pos = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], stack, owner);
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
if(movedStack)
|
||||
owner->addNewAnim(new CMovementEndAnimation(owner, stack, nextHex));
|
||||
owner->addNewAnim(new CMovementEndAnimation(owner, stack, nextHex));
|
||||
|
||||
if(owner->moveSh >= 0)
|
||||
if(owner->moveSoundHander != -1)
|
||||
{
|
||||
CCS->soundh->stopSound(owner->moveSh);
|
||||
owner->moveSh = -1;
|
||||
CCS->soundh->stopSound(owner->moveSoundHander);
|
||||
owner->moveSoundHander = -1;
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
CMovementAnimation::CMovementAnimation(CBattleInterface *_owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance)
|
||||
: CBattleStackAnimation(_owner, _stack), destTiles(_destTiles), nextPos(0), distance(_distance), stepX(0.0), stepY(0.0)
|
||||
: CBattleStackAnimation(_owner, _stack),
|
||||
destTiles(_destTiles),
|
||||
curentMoveIndex(0),
|
||||
oldPos(stack->position),
|
||||
nextHex(destTiles.front()),
|
||||
begX(0), begY(0),
|
||||
distanceX(0), distanceY(0),
|
||||
timeToMove(0.0),
|
||||
progress(0.0)
|
||||
{
|
||||
curStackPos = stack->position;
|
||||
nextHex = destTiles.front();
|
||||
}
|
||||
|
||||
CMovementEndAnimation::CMovementEndAnimation(CBattleInterface * _owner, const CStack * _stack, BattleHex destTile)
|
||||
@ -522,8 +467,8 @@ bool CMovementEndAnimation::init()
|
||||
if( !isEarliest(true) )
|
||||
return false;
|
||||
|
||||
if(!stack || myAnim()->framesInGroup(CCreatureAnim::MOVE_END) == 0 ||
|
||||
myAnim()->getType() == CCreatureAnim::DEATH)
|
||||
if(!stack || myAnim->framesInGroup(CCreatureAnim::MOVE_END) == 0 ||
|
||||
myAnim->isDead())
|
||||
{
|
||||
endAnim();
|
||||
|
||||
@ -532,25 +477,19 @@ bool CMovementEndAnimation::init()
|
||||
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), endMoving));
|
||||
|
||||
myAnim()->setType(CCreatureAnim::MOVE_END);
|
||||
myAnim->setType(CCreatureAnim::MOVE_END);
|
||||
|
||||
myAnim->onAnimationReset += std::bind(&CMovementEndAnimation::endAnim, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMovementEndAnimation::nextFrame()
|
||||
{
|
||||
if(myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
endAnim();
|
||||
}
|
||||
}
|
||||
|
||||
void CMovementEndAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
if(myAnim()->getType() != CCreatureAnim::DEATH)
|
||||
myAnim()->setType(CCreatureAnim::HOLDING); //resetting to default
|
||||
if(myAnim->getType() != CCreatureAnim::DEAD)
|
||||
myAnim->setType(CCreatureAnim::HOLDING); //resetting to default
|
||||
|
||||
CCS->curh->show();
|
||||
delete this;
|
||||
@ -566,31 +505,19 @@ bool CMovementStartAnimation::init()
|
||||
return false;
|
||||
|
||||
|
||||
if(!stack || myAnim()->getType() == CCreatureAnim::DEATH)
|
||||
if(!stack || myAnim->isDead())
|
||||
{
|
||||
CMovementStartAnimation::endAnim();
|
||||
return false;
|
||||
}
|
||||
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), startMoving));
|
||||
myAnim()->setType(CCreatureAnim::MOVE_START);
|
||||
myAnim->setType(CCreatureAnim::MOVE_START);
|
||||
myAnim->onAnimationReset += std::bind(&CMovementStartAnimation::endAnim, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMovementStartAnimation::nextFrame()
|
||||
{
|
||||
if(myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
endAnim();
|
||||
}
|
||||
else
|
||||
{
|
||||
if((owner->animCount+1)%(4/owner->getAnimSpeed())==0)
|
||||
myAnim()->incrementFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void CMovementStartAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
@ -599,63 +526,64 @@ void CMovementStartAnimation::endAnim()
|
||||
}
|
||||
|
||||
CReverseAnimation::CReverseAnimation(CBattleInterface * _owner, const CStack * stack, BattleHex dest, bool _priority)
|
||||
: CBattleStackAnimation(_owner, stack), partOfAnim(1), secondPartSetup(false), hex(dest), priority(_priority)
|
||||
: CBattleStackAnimation(_owner, stack), hex(dest), priority(_priority)
|
||||
{}
|
||||
|
||||
bool CReverseAnimation::init()
|
||||
{
|
||||
if(myAnim() == nullptr || myAnim()->getType() == CCreatureAnim::DEATH)
|
||||
if(myAnim == nullptr || myAnim->isDead())
|
||||
{
|
||||
endAnim();
|
||||
|
||||
return false; //there is no such creature
|
||||
}
|
||||
|
||||
if(!priority && !isEarliest(false))
|
||||
return false;
|
||||
|
||||
//myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
|
||||
//myAnim->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
|
||||
|
||||
if(myAnim()->framesInGroup(CCreatureAnim::TURN_L))
|
||||
myAnim()->setType(CCreatureAnim::TURN_L);
|
||||
if(myAnim->framesInGroup(CCreatureAnim::TURN_L))
|
||||
{
|
||||
myAnim->setType(CCreatureAnim::TURN_L);
|
||||
myAnim->onAnimationReset += std::bind(&CReverseAnimation::setupSecondPart, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
setupSecondPart();
|
||||
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CReverseAnimation::nextFrame()
|
||||
{
|
||||
if(partOfAnim == 1) //first part of animation
|
||||
{
|
||||
if(myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
partOfAnim = 2;
|
||||
}
|
||||
}
|
||||
else if(partOfAnim == 2)
|
||||
{
|
||||
if(!secondPartSetup)
|
||||
{
|
||||
setupSecondPart();
|
||||
}
|
||||
if(myAnim()->onLastFrameInGroup())
|
||||
{
|
||||
endAnim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CReverseAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
if( stack->alive() )//don't do that if stack is dead
|
||||
myAnim()->setType(CCreatureAnim::HOLDING);
|
||||
myAnim->setType(CCreatureAnim::HOLDING);
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CReverseAnimation::rotateStack(CBattleInterface * owner, const CStack * stack, BattleHex hex)
|
||||
{
|
||||
owner->creDir[stack->ID] = !owner->creDir[stack->ID];
|
||||
|
||||
owner->creAnims[stack->ID]->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
|
||||
|
||||
if(stack->doubleWide())
|
||||
{
|
||||
if(stack->attackerOwned)
|
||||
{
|
||||
if(!owner->creDir[stack->ID])
|
||||
owner->creAnims[stack->ID]->pos.x -= 44;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(owner->creDir[stack->ID])
|
||||
owner->creAnims[stack->ID]->pos.x += 44;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CReverseAnimation::setupSecondPart()
|
||||
{
|
||||
if(!stack)
|
||||
@ -664,28 +592,13 @@ void CReverseAnimation::setupSecondPart()
|
||||
return;
|
||||
}
|
||||
|
||||
owner->creDir[stack->ID] = !owner->creDir[stack->ID];
|
||||
rotateStack(owner, stack, hex);
|
||||
|
||||
myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
|
||||
|
||||
if(stack->doubleWide())
|
||||
if(myAnim->framesInGroup(CCreatureAnim::TURN_R))
|
||||
{
|
||||
if(stack->attackerOwned)
|
||||
{
|
||||
if(!owner->creDir[stack->ID])
|
||||
myAnim()->pos.x -= 44;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(owner->creDir[stack->ID])
|
||||
myAnim()->pos.x += 44;
|
||||
}
|
||||
myAnim->setType(CCreatureAnim::TURN_R);
|
||||
myAnim->onAnimationReset += std::bind(&CReverseAnimation::endAnim, this);
|
||||
}
|
||||
|
||||
secondPartSetup = true;
|
||||
|
||||
if(myAnim()->framesInGroup(CCreatureAnim::TURN_R))
|
||||
myAnim()->setType(CCreatureAnim::TURN_R);
|
||||
else
|
||||
endAnim();
|
||||
}
|
||||
@ -701,7 +614,7 @@ bool CShootingAnimation::init()
|
||||
|
||||
const CStack * shooter = attackingStack;
|
||||
|
||||
if(!shooter || myAnim()->getType() == CCreatureAnim::DEATH)
|
||||
if(!shooter || myAnim->isDead())
|
||||
{
|
||||
endAnim();
|
||||
return false;
|
||||
@ -730,6 +643,7 @@ bool CShootingAnimation::init()
|
||||
}
|
||||
|
||||
ProjectileInfo spi;
|
||||
spi.shotDone = false;
|
||||
spi.creID = shooter->getCreature()->idNumber;
|
||||
spi.stackID = shooter->ID;
|
||||
// reverse if creature is facing right OR this is non-existing stack that is not tower (war machines)
|
||||
@ -783,7 +697,7 @@ bool CShootingAnimation::init()
|
||||
|
||||
if (attackedStack)
|
||||
{
|
||||
double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile
|
||||
double animSpeed = AnimationControls::getProjectileSpeed(); // flight speed of projectile
|
||||
spi.lastStep = static_cast<int>(sqrt(static_cast<double>((destPos.x - spi.x) * (destPos.x - spi.x) + (destPos.y - spi.y) * (destPos.y - spi.y))) / animSpeed);
|
||||
if(spi.lastStep == 0)
|
||||
spi.lastStep = 1;
|
||||
@ -795,7 +709,7 @@ bool CShootingAnimation::init()
|
||||
// Catapult attack
|
||||
spi.catapultInfo.reset(new CatapultProjectileInfo(Point(spi.x, spi.y), destPos));
|
||||
|
||||
double animSpeed = 3.318 * owner->getAnimSpeed();
|
||||
double animSpeed = AnimationControls::getProjectileSpeed() / 10;
|
||||
spi.lastStep = abs((destPos.x - spi.x) / animSpeed);
|
||||
spi.dx = animSpeed;
|
||||
spi.dy = 0;
|
||||
@ -854,10 +768,10 @@ bool CShootingAnimation::init()
|
||||
|
||||
void CShootingAnimation::nextFrame()
|
||||
{
|
||||
for(std::list<std::pair<CBattleAnimation *, bool> >::const_iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
|
||||
for(auto & it : owner->pendingAnims)
|
||||
{
|
||||
CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it->first);
|
||||
CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it->first);
|
||||
CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it.first);
|
||||
CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it.first);
|
||||
if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
|
||||
return;
|
||||
}
|
||||
@ -867,7 +781,7 @@ void CShootingAnimation::nextFrame()
|
||||
|
||||
void CShootingAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
CAttackAnimation::endAnim();
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,14 @@ class CBattleAnimation
|
||||
protected:
|
||||
CBattleInterface * owner;
|
||||
public:
|
||||
virtual bool init()=0; //to be called - if returned false, call again until returns true
|
||||
virtual void nextFrame()=0; //call every new frame
|
||||
virtual bool init() = 0; //to be called - if returned false, call again until returns true
|
||||
virtual void nextFrame() {} //call every new frame
|
||||
virtual void endAnim(); //to be called mostly internally; in this class it removes animation from pendingAnims list
|
||||
virtual ~CBattleAnimation(){};
|
||||
virtual ~CBattleAnimation(){}
|
||||
|
||||
bool isEarliest(bool perStackConcurrency); //determines if this animation is earliest of all
|
||||
|
||||
ui32 ID; //unique identifier
|
||||
|
||||
CBattleAnimation(CBattleInterface * _owner);
|
||||
};
|
||||
|
||||
@ -41,16 +40,17 @@ public:
|
||||
class CBattleStackAnimation : public CBattleAnimation
|
||||
{
|
||||
public:
|
||||
CCreatureAnimation * myAnim; //animation for our stack, managed by CBattleInterface
|
||||
const CStack * stack; //id of stack whose animation it is
|
||||
|
||||
CBattleStackAnimation(CBattleInterface * _owner, const CStack * _stack);
|
||||
|
||||
CCreatureAnimation * myAnim(); //animation for our stack
|
||||
};
|
||||
|
||||
/// This class is responsible for managing the battle attack animation
|
||||
class CAttackAnimation : public CBattleStackAnimation
|
||||
{
|
||||
bool soundPlayed;
|
||||
|
||||
protected:
|
||||
BattleHex dest; //attacked hex
|
||||
bool shooting;
|
||||
@ -60,6 +60,7 @@ protected:
|
||||
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
|
||||
public:
|
||||
void nextFrame();
|
||||
void endAnim();
|
||||
bool checkInitialConditions();
|
||||
|
||||
CAttackAnimation(CBattleInterface *_owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
|
||||
@ -101,7 +102,6 @@ class CMeleeAttackAnimation : public CAttackAnimation
|
||||
{
|
||||
public:
|
||||
bool init();
|
||||
void nextFrame();
|
||||
void endAnim();
|
||||
|
||||
CMeleeAttackAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked);
|
||||
@ -112,14 +112,20 @@ public:
|
||||
class CMovementAnimation : public CBattleStackAnimation
|
||||
{
|
||||
private:
|
||||
std::vector<BattleHex> destTiles; //destination
|
||||
BattleHex nextHex;
|
||||
ui32 nextPos;
|
||||
int distance;
|
||||
double stepX, stepY; //how far stack is moved in one frame
|
||||
double posX, posY;
|
||||
int steps, whichStep;
|
||||
int curStackPos; //position of stack before move
|
||||
bool shouldRotate();
|
||||
|
||||
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
|
||||
ui32 curentMoveIndex; // index of nextHex in destTiles
|
||||
|
||||
BattleHex oldPos; //position of stack before move
|
||||
BattleHex nextHex; // next hex, to which creature move right now
|
||||
|
||||
double begX, begY; // starting position
|
||||
double distanceX, distanceY; // full movement distance, may be negative if creture moves topleft
|
||||
|
||||
double timeToMove; // full length of movement animation
|
||||
double progress; // range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
|
||||
|
||||
public:
|
||||
bool init();
|
||||
void nextFrame();
|
||||
@ -136,7 +142,6 @@ private:
|
||||
BattleHex destinationTile;
|
||||
public:
|
||||
bool init();
|
||||
void nextFrame();
|
||||
void endAnim();
|
||||
|
||||
CMovementEndAnimation(CBattleInterface * _owner, const CStack * _stack, BattleHex destTile);
|
||||
@ -148,7 +153,6 @@ class CMovementStartAnimation : public CBattleStackAnimation
|
||||
{
|
||||
public:
|
||||
bool init();
|
||||
void nextFrame();
|
||||
void endAnim();
|
||||
|
||||
CMovementStartAnimation(CBattleInterface * _owner, const CStack * _stack);
|
||||
@ -158,14 +162,12 @@ public:
|
||||
/// Class responsible for animation of stack chaning direction (left <-> right)
|
||||
class CReverseAnimation : public CBattleStackAnimation
|
||||
{
|
||||
private:
|
||||
int partOfAnim; //1 - first, 2 - second
|
||||
bool secondPartSetup;
|
||||
BattleHex hex;
|
||||
public:
|
||||
bool priority; //true - high, false - low
|
||||
bool init();
|
||||
void nextFrame();
|
||||
|
||||
static void rotateStack(CBattleInterface * owner, const CStack * stack, BattleHex hex);
|
||||
|
||||
void setupSecondPart();
|
||||
void endAnim();
|
||||
@ -184,7 +186,8 @@ struct ProjectileInfo
|
||||
int stackID; //ID of stack
|
||||
int frameNum; //frame to display form projectile animation
|
||||
//bool spin; //if true, frameNum will be increased
|
||||
int animStartDelay; //how many times projectile must be attempted to be shown till it's really show (decremented after hit)
|
||||
int animStartDelay; //frame of shooter animation when projectile should appear
|
||||
bool shotDone; // actual shot already done, projectile is flying
|
||||
bool reverse; //if true, projectile will be flipped by vertical asix
|
||||
std::shared_ptr<CatapultProjectileInfo> catapultInfo; // holds info about the parabolic trajectory of the cannon
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../CAdvmapInterface.h"
|
||||
#include "../CAnimation.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../../lib/CObjectHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../CDefHandler.h"
|
||||
@ -43,8 +44,6 @@ const double M_PI = 3.14159265358979323846;
|
||||
|
||||
using namespace boost::assign;
|
||||
|
||||
const time_t CBattleInterface::HOVER_ANIM_DELTA = 1;
|
||||
|
||||
/*
|
||||
* CBattleInterface.cpp, part of VCMI engine
|
||||
*
|
||||
@ -57,13 +56,27 @@ const time_t CBattleInterface::HOVER_ANIM_DELTA = 1;
|
||||
|
||||
CondSh<bool> CBattleInterface::animsAreDisplayed;
|
||||
|
||||
struct CMP_stack2
|
||||
static void onAnimationFinished(const CStack *stack, CCreatureAnimation * anim)
|
||||
{
|
||||
inline bool operator ()(const CStack& a, const CStack& b)
|
||||
if (anim->isIdle())
|
||||
{
|
||||
return (a.Speed())>(b.Speed());
|
||||
const CCreature *creature = stack->getCreature();
|
||||
|
||||
if (anim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
|
||||
{
|
||||
if (float(rand() % 100) < creature->animation.timeBetweenFidgets * 10)
|
||||
anim->playOnce(CCreatureAnim::MOUSEON);
|
||||
else
|
||||
anim->setType(CCreatureAnim::HOLDING);
|
||||
}
|
||||
else
|
||||
{
|
||||
anim->setType(CCreatureAnim::HOLDING);
|
||||
}
|
||||
}
|
||||
} cmpst2 ;
|
||||
// always reset callback
|
||||
anim->onAnimationReset += std::bind(&onAnimationFinished, stack, anim);
|
||||
}
|
||||
|
||||
static void transformPalette(SDL_Surface * surf, double rCor, double gCor, double bCor)
|
||||
{
|
||||
@ -80,7 +93,6 @@ static void transformPalette(SDL_Surface * surf, double rCor, double gCor, doubl
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////
|
||||
|
||||
void CBattleInterface::addNewAnim(CBattleAnimation * anim)
|
||||
{
|
||||
@ -93,10 +105,10 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
|
||||
const SDL_Rect & myRect,
|
||||
shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen)
|
||||
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
||||
activeStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
|
||||
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
|
||||
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(nullptr), sp(nullptr),
|
||||
siegeH(nullptr), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
|
||||
givenCommand(nullptr), myTurn(false), resWindow(nullptr), moveStarted(false), moveSh(-1), bresult(nullptr)
|
||||
givenCommand(nullptr), myTurn(false), resWindow(nullptr), moveStarted(false), moveSoundHander(-1), bresult(nullptr)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
|
||||
@ -719,7 +731,7 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
const CStack *s = stack;
|
||||
if(creAnims.find(s->ID) == creAnims.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
if(creAnims[s->ID]->getType() != CCreatureAnim::DEATH && s->position >= 0) //don't show turrets here
|
||||
if(creAnims[s->ID]->getType() != CCreatureAnim::DEAD && s->position >= 0) //don't show turrets here
|
||||
stackAliveByHex[s->position].push_back(s);
|
||||
}
|
||||
std::vector<const CStack *> stackDeadByHex[GameConstants::BFIELD_SIZE];
|
||||
@ -728,7 +740,7 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
const CStack *s = stack;
|
||||
if(creAnims.find(s->ID) == creAnims.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
if(creAnims[s->ID]->getType() == CCreatureAnim::DEATH)
|
||||
if(creAnims[s->ID]->isDead())
|
||||
stackDeadByHex[s->position].push_back(s);
|
||||
}
|
||||
|
||||
@ -790,7 +802,8 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
{
|
||||
for(size_t v=0; v<elem.size(); ++v)
|
||||
{
|
||||
creAnims[elem[v]->ID]->nextFrame(to, creAnims[elem[v]->ID]->pos.x, creAnims[elem[v]->ID]->pos.y, creDir[elem[v]->ID], animCount, false); //increment always when moving, never if stack died
|
||||
creAnims[elem[v]->ID]->nextFrame(to, creAnims[elem[v]->ID]->pos.x, creAnims[elem[v]->ID]->pos.y, creDir[elem[v]->ID]);
|
||||
creAnims[elem[v]->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
}
|
||||
}
|
||||
std::vector<const CStack *> flyingStacks; //flying stacks should be displayed later, over other stacks and obstacles
|
||||
@ -964,7 +977,7 @@ void CBattleInterface::showAliveStacks(std::vector<const CStack *> *aliveStacks,
|
||||
{
|
||||
const CStack *s = elem;
|
||||
|
||||
if(!s->hasBonusOfType(Bonus::FLYING) || creAnims[s->ID]->getType() != CCreatureAnim::DEATH)
|
||||
if(!s->hasBonusOfType(Bonus::FLYING) || creAnims[s->ID]->getType() != CCreatureAnim::DEAD)
|
||||
showAliveStack(s, to);
|
||||
else
|
||||
flyingStacks->push_back(s);
|
||||
@ -1376,8 +1389,9 @@ void CBattleInterface::newStack(const CStack * stack)
|
||||
|
||||
if(stack->position < 0) //turret
|
||||
{
|
||||
const CCreature & turretCreature = *CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
|
||||
creAnims[stack->ID] = new CCreatureAnimation(turretCreature.animDefName);
|
||||
const CCreature * turretCreature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
|
||||
|
||||
creAnims[stack->ID] = AnimationControls::getAnimation(turretCreature);
|
||||
|
||||
// Turret positions are read out of the /config/wall_pos.txt
|
||||
int posID = 0;
|
||||
@ -1399,13 +1413,18 @@ void CBattleInterface::newStack(const CStack * stack)
|
||||
coords.x = siegeH->town->town->clientInfo.siegePositions[posID].x + this->pos.x;
|
||||
coords.y = siegeH->town->town->clientInfo.siegePositions[posID].y + this->pos.y;
|
||||
}
|
||||
creAnims[stack->ID]->pos.h = siegeH->town->town->clientInfo.siegeShooterCropHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
creAnims[stack->ID] = new CCreatureAnimation(stack->getCreature()->animDefName);
|
||||
creAnims[stack->ID] = AnimationControls::getAnimation(stack->getCreature());
|
||||
creAnims[stack->ID]->onAnimationReset += std::bind(&onAnimationFinished, stack, creAnims[stack->ID]);
|
||||
creAnims[stack->ID]->pos.h = creAnims[stack->ID]->getHeight();
|
||||
}
|
||||
creAnims[stack->ID]->pos.x = coords.x;
|
||||
creAnims[stack->ID]->pos.y = coords.y;
|
||||
creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
|
||||
creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
|
||||
creAnims[stack->ID]->pos = Rect(coords.x, coords.y, creAnims[stack->ID]->fullWidth, creAnims[stack->ID]->fullHeight);
|
||||
creDir[stack->ID] = stack->attackerOwned;
|
||||
|
||||
}
|
||||
@ -1541,14 +1560,14 @@ void CBattleInterface::giveCommand(Battle::ActionType action, BattleHex tile, ui
|
||||
{
|
||||
logGlobal->traceStream() << "Setting command for " << (stack ? stack->nodeName() : "hero");
|
||||
myTurn = false;
|
||||
activeStack = nullptr;
|
||||
setActiveStack(nullptr);
|
||||
givenCommand->setn(ba);
|
||||
}
|
||||
else
|
||||
{
|
||||
curInt->cb->battleMakeTacticAction(ba);
|
||||
vstd::clear_pointer(ba);
|
||||
activeStack = nullptr;
|
||||
setActiveStack(nullptr);
|
||||
//next stack will be activated when action ends
|
||||
}
|
||||
}
|
||||
@ -1618,7 +1637,7 @@ void CBattleInterface::battleFinished(const BattleResult& br)
|
||||
animsAreDisplayed.waitUntil(false);
|
||||
}
|
||||
displayBattleFinished();
|
||||
activeStack = nullptr;
|
||||
setActiveStack(nullptr);
|
||||
}
|
||||
|
||||
void CBattleInterface::displayBattleFinished()
|
||||
@ -2041,17 +2060,49 @@ void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte)
|
||||
void CBattleInterface::setAnimSpeed(int set)
|
||||
{
|
||||
Settings speed = settings.write["battle"]["animationSpeed"];
|
||||
speed->Float() = set;
|
||||
speed->Float() = float(set) / 100;
|
||||
}
|
||||
|
||||
int CBattleInterface::getAnimSpeed() const
|
||||
{
|
||||
return settings["battle"]["animationSpeed"].Float();
|
||||
return round(settings["battle"]["animationSpeed"].Float() * 100);
|
||||
}
|
||||
|
||||
void CBattleInterface::setActiveStack(const CStack * stack)
|
||||
{
|
||||
if (activeStack) // update UI
|
||||
creAnims[activeStack->ID]->setBorderColor(AnimationControls::getNoBorder());
|
||||
|
||||
activeStack = stack;
|
||||
|
||||
if (activeStack) // update UI
|
||||
creAnims[activeStack->ID]->setBorderColor(AnimationControls::getGoldBorder());
|
||||
}
|
||||
|
||||
void CBattleInterface::setHoveredStack(const CStack * stack)
|
||||
{
|
||||
if (mouseHoveredStack)
|
||||
creAnims[mouseHoveredStack->ID]->setBorderColor(AnimationControls::getNoBorder());
|
||||
|
||||
// stack must be alive and not active (which uses gold border instead)
|
||||
if (stack && stack->alive() && stack != activeStack)
|
||||
{
|
||||
mouseHoveredStack = stack;
|
||||
|
||||
if (mouseHoveredStack)
|
||||
{
|
||||
creAnims[mouseHoveredStack->ID]->setBorderColor(AnimationControls::getBlueBorder());
|
||||
if (creAnims[mouseHoveredStack->ID]->framesInGroup(CCreatureAnim::MOUSEON) > 0)
|
||||
creAnims[mouseHoveredStack->ID]->playOnce(CCreatureAnim::MOUSEON);
|
||||
}
|
||||
}
|
||||
else
|
||||
mouseHoveredStack = nullptr;
|
||||
}
|
||||
|
||||
void CBattleInterface::activateStack()
|
||||
{
|
||||
activeStack = stackToActivate;
|
||||
setActiveStack(stackToActivate);
|
||||
stackToActivate = nullptr;
|
||||
const CStack *s = activeStack;
|
||||
|
||||
@ -2098,21 +2149,6 @@ void CBattleInterface::activateStack()
|
||||
GH.fakeMouseMove();
|
||||
}
|
||||
|
||||
double CBattleInterface::getAnimSpeedMultiplier() const
|
||||
{
|
||||
switch(getAnimSpeed())
|
||||
{
|
||||
case 1:
|
||||
return 3.5;
|
||||
case 2:
|
||||
return 2.2;
|
||||
case 4:
|
||||
return 1.0;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleInterface::endCastingSpell()
|
||||
{
|
||||
assert(spellDestSelectMode);
|
||||
@ -2204,63 +2240,9 @@ void CBattleInterface::showAliveStack(const CStack *stack, SDL_Surface * to)
|
||||
int ID = stack->ID;
|
||||
if(creAnims.find(ID) == creAnims.end()) //eg. for summoned but not yet handled stacks
|
||||
return;
|
||||
const CCreature *creature = stack->getCreature();
|
||||
SDL_Rect unitRect = {creAnims[ID]->pos.x, creAnims[ID]->pos.y, uint16_t(creAnims[ID]->fullWidth), uint16_t(creAnims[ID]->fullHeight)};
|
||||
|
||||
CCreatureAnim::EAnimType animType = creAnims[ID]->getType();
|
||||
|
||||
int affectingSpeed = getAnimSpeed();
|
||||
if(animType == CCreatureAnim::MOUSEON || animType == CCreatureAnim::HOLDING) //standing stacks should not stand faster :)
|
||||
affectingSpeed = 2;
|
||||
bool incrementFrame = (animCount%(4/affectingSpeed)==0) && animType!=CCreatureAnim::DEATH &&
|
||||
animType!=CCreatureAnim::MOVE_START && animType!=CCreatureAnim::HOLDING;
|
||||
|
||||
if (creature->idNumber == CreatureID::ARROW_TOWERS)
|
||||
{
|
||||
// a turret creature has a limited height, so cut it at a certain position; turret creature has no standing anim
|
||||
unitRect.h = siegeH->town->town->clientInfo.siegeShooterCropHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// standing animation
|
||||
if(animType == CCreatureAnim::HOLDING)
|
||||
{
|
||||
if(standingFrame.find(ID)!=standingFrame.end())
|
||||
{
|
||||
incrementFrame = (animCount%(8/affectingSpeed)==0);
|
||||
if(incrementFrame)
|
||||
{
|
||||
++standingFrame[ID];
|
||||
if(standingFrame[ID] == creAnims[ID]->framesInGroup(CCreatureAnim::HOLDING))
|
||||
{
|
||||
standingFrame.erase(standingFrame.find(ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((rand()%50) == 0)
|
||||
{
|
||||
standingFrame.insert(std::make_pair(ID, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As long as the projectile of the shooter-stack is flying incrementFrame should be false
|
||||
//bool shootingFinished = true;
|
||||
for (auto & elem : projectiles)
|
||||
{
|
||||
if (elem.stackID == ID)
|
||||
{
|
||||
//shootingFinished = false;
|
||||
if (elem.animStartDelay == 0)
|
||||
incrementFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Increment always when moving, never if stack died
|
||||
creAnims[ID]->nextFrame(to, unitRect.x, unitRect.y, creDir[ID], animCount, incrementFrame, activeStack && ID==activeStack->ID, ID==mouseHoveredStack, &unitRect);
|
||||
creAnims[ID]->nextFrame(to, creAnims[ID]->pos.x, creAnims[ID]->pos.y, creDir[ID], nullptr);
|
||||
creAnims[ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
|
||||
//printing amount
|
||||
if(stack->count > 0 //don't print if stack is not alive
|
||||
@ -2471,17 +2453,19 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
|
||||
if(to == nullptr)
|
||||
to = screen;
|
||||
std::list< std::list<ProjectileInfo>::iterator > toBeDeleted;
|
||||
for(auto it=projectiles.begin(); it!=projectiles.end(); ++it)
|
||||
for(auto it = projectiles.begin(); it!=projectiles.end(); ++it)
|
||||
{
|
||||
// Creature have to be in a shooting anim and the anim start delay must be over.
|
||||
// Otherwise abort to start moving the projectile.
|
||||
if (it->animStartDelay > 0)
|
||||
// Check if projectile is already visible (shooter animation did the shot)
|
||||
if (!it->shotDone)
|
||||
{
|
||||
if(it->animStartDelay == creAnims[it->stackID]->getAnimationFrame() + 1
|
||||
&& creAnims[it->stackID]->getType() >= 14 && creAnims[it->stackID]->getType() <= 16)
|
||||
it->animStartDelay = 0;
|
||||
if (creAnims[it->stackID]->getCurrentFrame() > it->animStartDelay)
|
||||
{
|
||||
//at this point projectile should become visible
|
||||
creAnims[it->stackID]->pause(); // pause animation
|
||||
it->shotDone = true;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
continue; // wait...
|
||||
}
|
||||
|
||||
SDL_Rect dst;
|
||||
@ -2528,6 +2512,8 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
|
||||
}
|
||||
for(auto & elem : toBeDeleted)
|
||||
{
|
||||
// resume animation
|
||||
creAnims[elem->stackID]->play();
|
||||
projectiles.erase(elem);
|
||||
}
|
||||
}
|
||||
@ -2545,24 +2531,19 @@ void CBattleInterface::endAction(const BattleAction* action)
|
||||
}
|
||||
|
||||
if(stack && action->actionType == Battle::WALK &&
|
||||
creAnims[action->stackNumber]->getType() != CCreatureAnim::HOLDING) //walk or walk & attack
|
||||
!creAnims[action->stackNumber]->isIdle()) //walk or walk & attack
|
||||
{
|
||||
pendingAnims.push_back(std::make_pair(new CMovementEndAnimation(this, stack, action->destinationTile), false));
|
||||
}
|
||||
if(action->actionType == Battle::CATAPULT) //catapult
|
||||
{
|
||||
}
|
||||
|
||||
//check if we should reverse stacks
|
||||
//for some strange reason, it's not enough
|
||||
// std::set<const CStack *> stacks;
|
||||
// stacks.insert(LOCPLINT->cb->battleGetStackByID(action->stackNumber));
|
||||
// stacks.insert(LOCPLINT->cb->battleGetStackByPos(action->destinationTile));
|
||||
TStacks stacks = curInt->cb->battleGetStacks(CBattleCallback::MINE_AND_ENEMY);
|
||||
|
||||
for(const CStack *s : stacks)
|
||||
{
|
||||
if(s && creDir[s->ID] != bool(s->attackerOwned) && s->alive())
|
||||
if(s && creDir[s->ID] != bool(s->attackerOwned) && s->alive()
|
||||
&& creAnims[s->ID]->isIdle())
|
||||
{
|
||||
addNewAnim(new CReverseAnimation(this, s, s->position, false));
|
||||
}
|
||||
@ -2613,6 +2594,9 @@ void CBattleInterface::showQueue()
|
||||
|
||||
void CBattleInterface::startAction(const BattleAction* action)
|
||||
{
|
||||
setActiveStack(nullptr);
|
||||
setHoveredStack(nullptr);
|
||||
|
||||
if(action->actionType == Battle::END_TACTIC_PHASE)
|
||||
{
|
||||
SDL_FreeSurface(menu);
|
||||
@ -2719,7 +2703,7 @@ void CBattleInterface::waitForAnims()
|
||||
|
||||
void CBattleInterface::bEndTacticPhase()
|
||||
{
|
||||
activeStack = nullptr;
|
||||
setActiveStack(nullptr);
|
||||
btactEnd->block(true);
|
||||
tacticsMode = false;
|
||||
}
|
||||
@ -2839,9 +2823,12 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
||||
if (shere)
|
||||
ourStack = shere->owner == curInt->playerID;
|
||||
|
||||
//TODO: handle
|
||||
bool noStackIsHovered = true; //will cause removing a blue glow
|
||||
|
||||
//stack changed, update selection border
|
||||
if (shere != mouseHoveredStack)
|
||||
{
|
||||
setHoveredStack(shere);
|
||||
}
|
||||
|
||||
localActions.clear();
|
||||
illegalActions.clear();
|
||||
|
||||
@ -3145,23 +3132,8 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
||||
cursorFrame = ECursor::COMBAT_QUERY;
|
||||
consoleMsg = (boost::format(CGI->generaltexth->allTexts[297]) % shere->getName()).str();
|
||||
realizeAction = [=]{ GH.pushInt(createCreWindow(shere, true)); };
|
||||
|
||||
//setting console text
|
||||
const time_t curTime = time(nullptr);
|
||||
CCreatureAnimation *hoveredStackAnim = creAnims[shere->ID];
|
||||
|
||||
if (shere->ID != mouseHoveredStack
|
||||
&& curTime > lastMouseHoveredStackAnimationTime + HOVER_ANIM_DELTA
|
||||
&& hoveredStackAnim->getType() == CCreatureAnim::HOLDING
|
||||
&& hoveredStackAnim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
|
||||
{
|
||||
hoveredStackAnim->playOnce(CCreatureAnim::MOUSEON);
|
||||
lastMouseHoveredStackAnimationTime = curTime;
|
||||
}
|
||||
noStackIsHovered = false;
|
||||
mouseHoveredStack = shere->ID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else //no possible valid action, display message
|
||||
@ -3249,11 +3221,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
realizeThingsToDo();
|
||||
if(noStackIsHovered)
|
||||
mouseHoveredStack = -1;
|
||||
|
||||
}
|
||||
|
||||
bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber)
|
||||
@ -3447,7 +3415,7 @@ Rect CBattleInterface::hexPosition(BattleHex hex) const
|
||||
|
||||
SDL_Surface * CBattleInterface::imageOfObstacle(const CObstacleInstance &oi) const
|
||||
{
|
||||
int frameIndex = (animCount+1) / (40/getAnimSpeed());
|
||||
int frameIndex = (animCount+1) * 25 / getAnimSpeed();
|
||||
switch(oi.obstacleType)
|
||||
{
|
||||
case CObstacleInstance::USUAL:
|
||||
|
@ -125,20 +125,16 @@ private:
|
||||
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
|
||||
const CStack * mouseHoveredStack; // stack below mouse pointer, used for border animation
|
||||
const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; nullptr of none
|
||||
const CStack * selectedStack; //for Teleport / Sacrifice
|
||||
void activateStack(); //sets activeStack to stackToActivate etc.
|
||||
int mouseHoveredStack; //stack hovered by mouse; if -1 -> none
|
||||
time_t lastMouseHoveredStackAnimationTime; // time when last mouse hovered animation occurred
|
||||
static const time_t HOVER_ANIM_DELTA;
|
||||
std::vector<BattleHex> occupyableHexes, //hexes available for active stack
|
||||
attackableHexes; //hexes attackable by active stack
|
||||
bool stackCountOutsideHexes[GameConstants::BFIELD_SIZE]; // hexes that when in front of a unit cause it's amount box to move back
|
||||
int previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago
|
||||
int currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon)
|
||||
int attackingHex; //hex from which the stack would perform attack with current cursor
|
||||
double getAnimSpeedMultiplier() const; //returns multiplier for number of frames in a group
|
||||
std::map<int, int> standingFrame; //number of frame in standing animation by stack ID, helps in showing 'random moves'
|
||||
|
||||
shared_ptr<CPlayerInterface> tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
|
||||
bool tacticsMode;
|
||||
@ -156,6 +152,9 @@ private:
|
||||
PossibleActions selectedAction; //last action chosen (and saved) by player
|
||||
PossibleActions illegalAction; //most likely action that can't be performed here
|
||||
|
||||
void setActiveStack(const CStack * stack);
|
||||
void setHoveredStack(const CStack * stack);
|
||||
|
||||
void requestAutofightingAIToTakeAction();
|
||||
|
||||
void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn
|
||||
@ -214,8 +213,8 @@ public:
|
||||
void setPrintCellBorders(bool set); //if true, cell borders will be printed
|
||||
void setPrintStackRange(bool set); //if true,range of active stack will be printed
|
||||
void setPrintMouseShadow(bool set); //if true, hex under mouse will be shaded
|
||||
void setAnimSpeed(int set); //speed of animation; 1 - slowest, 2 - medium, 4 - fastest
|
||||
int getAnimSpeed() const; //speed of animation; 1 - slowest, 2 - medium, 4 - fastest
|
||||
void setAnimSpeed(int set); //speed of animation; range 1..100
|
||||
int getAnimSpeed() const; //speed of animation; range 1..100
|
||||
|
||||
std::vector<CClickableHex*> bfield; //11 lines, 17 hexes on each
|
||||
//std::vector< CBattleObstacle * > obstacles; //vector of obstacles on the battlefield
|
||||
@ -225,7 +224,7 @@ public:
|
||||
CBattleResultWindow * resWindow; //window of end of battle
|
||||
|
||||
bool moveStarted; //if true, the creature that is already moving is going to make its first step
|
||||
int moveSh; // sound handler used when moving a unit
|
||||
int moveSoundHander; // sound handler used when moving a unit
|
||||
|
||||
const BattleResult * bresult; //result of a battle; if non-zero then display when all animations end
|
||||
|
||||
|
@ -256,9 +256,9 @@ CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInt
|
||||
mouseShadow->select(settings["battle"]["mouseShadow"].Bool());
|
||||
|
||||
animSpeeds = new CHighlightableButtonsGroup(0);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[422].first),CGI->generaltexth->zelp[422].second, "sysopb9.def", 28, 225, 1);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[423].first),CGI->generaltexth->zelp[423].second, "sysob10.def", 92, 225, 2);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[424].first),CGI->generaltexth->zelp[424].second, "sysob11.def",156, 225, 4);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[422].first),CGI->generaltexth->zelp[422].second, "sysopb9.def", 28, 225, 40);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[423].first),CGI->generaltexth->zelp[423].second, "sysob10.def", 92, 225, 63);
|
||||
animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[424].first),CGI->generaltexth->zelp[424].second, "sysob11.def",156, 225, 100);
|
||||
animSpeeds->select(owner->getAnimSpeed(), 1);
|
||||
animSpeeds->onChange = boost::bind(&CBattleInterface::setAnimSpeed, owner, _1);
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
#include "StdInc.h"
|
||||
#include "CCreatureAnimation.h"
|
||||
|
||||
#include "../../lib/filesystem/CResourceLoader.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/vcmi_endian.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
|
||||
#include "../../lib/filesystem/CResourceLoader.h"
|
||||
#include "../../lib/filesystem/CBinaryReader.h"
|
||||
#include "../../lib/filesystem/CMemoryStream.h"
|
||||
|
||||
/*
|
||||
* CCreatureAnimation.cpp, part of VCMI engine
|
||||
*
|
||||
@ -17,6 +21,100 @@
|
||||
*
|
||||
*/
|
||||
|
||||
static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
|
||||
static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };
|
||||
static const SDL_Color creatureNoBorder = { 0, 0, 0, 0 };
|
||||
|
||||
SDL_Color AnimationControls::getBlueBorder()
|
||||
{
|
||||
return creatureBlueBorder;
|
||||
}
|
||||
|
||||
SDL_Color AnimationControls::getGoldBorder()
|
||||
{
|
||||
return creatureGoldBorder;
|
||||
}
|
||||
|
||||
SDL_Color AnimationControls::getNoBorder()
|
||||
{
|
||||
return creatureNoBorder;
|
||||
}
|
||||
|
||||
CCreatureAnimation * AnimationControls::getAnimation(const CCreature * creature)
|
||||
{
|
||||
auto func = boost::bind(&AnimationControls::getCreatureAnimationSpeed, creature, _1, _2);
|
||||
return new CCreatureAnimation(creature->animDefName, func);
|
||||
}
|
||||
|
||||
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CCreatureAnimation * anim, size_t group)
|
||||
{
|
||||
// possible new fields for creature format
|
||||
//Shoot Animation Time
|
||||
//Cast Animation Time
|
||||
//Defence and/or Death Animation Time
|
||||
|
||||
|
||||
// a lot of arbitrary multipliers, mostly to make animation speed closer to H3
|
||||
CCreatureAnim::EAnimType type = CCreatureAnim::EAnimType(group);
|
||||
const float baseSpeed = 10;
|
||||
const float speedMult = settings["battle"]["animationSpeed"].Float() * 20;
|
||||
const float speed = baseSpeed * speedMult;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CCreatureAnim::MOVING:
|
||||
return speed / creature->animation.walkAnimationTime / anim->framesInGroup(type);
|
||||
|
||||
case CCreatureAnim::MOUSEON:
|
||||
case CCreatureAnim::HOLDING:
|
||||
return baseSpeed;
|
||||
|
||||
case CCreatureAnim::ATTACK_UP:
|
||||
case CCreatureAnim::ATTACK_FRONT:
|
||||
case CCreatureAnim::ATTACK_DOWN:
|
||||
case CCreatureAnim::SHOOT_UP:
|
||||
case CCreatureAnim::SHOOT_FRONT:
|
||||
case CCreatureAnim::SHOOT_DOWN:
|
||||
case CCreatureAnim::CAST_UP:
|
||||
case CCreatureAnim::CAST_FRONT:
|
||||
case CCreatureAnim::CAST_DOWN:
|
||||
return speed * 2 / creature->animation.attackAnimationTime / anim->framesInGroup(type);
|
||||
|
||||
case CCreatureAnim::TURN_L:
|
||||
case CCreatureAnim::TURN_R:
|
||||
return speed;
|
||||
|
||||
case CCreatureAnim::MOVE_START:
|
||||
case CCreatureAnim::MOVE_END:
|
||||
return speed / 5;
|
||||
|
||||
case CCreatureAnim::HITTED:
|
||||
case CCreatureAnim::DEFENCE:
|
||||
case CCreatureAnim::DEATH:
|
||||
case CCreatureAnim::DEAD:
|
||||
return speed / 5;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
float AnimationControls::getProjectileSpeed()
|
||||
{
|
||||
return settings["battle"]["animationSpeed"].Float() * 100;
|
||||
}
|
||||
|
||||
float AnimationControls::getMovementDuration(const CCreature * creature)
|
||||
{
|
||||
return settings["battle"]["animationSpeed"].Float() * 4 / creature->animation.walkAnimationTime;
|
||||
}
|
||||
|
||||
float AnimationControls::getFlightDistance(const CCreature * creature)
|
||||
{
|
||||
return creature->animation.flightAnimationDistance * 200;
|
||||
}
|
||||
|
||||
CCreatureAnim::EAnimType CCreatureAnimation::getType() const
|
||||
{
|
||||
return type;
|
||||
@ -24,325 +122,313 @@ CCreatureAnim::EAnimType CCreatureAnimation::getType() const
|
||||
|
||||
void CCreatureAnimation::setType(CCreatureAnim::EAnimType type)
|
||||
{
|
||||
assert(framesInGroup(type) > 0 && "Bad type for void CCreatureAnimation::setType(int type)!");
|
||||
assert(type >= 0);
|
||||
assert(framesInGroup(type) != 0);
|
||||
|
||||
this->type = type;
|
||||
internalFrame = 0;
|
||||
if(type!=-1)
|
||||
{
|
||||
curFrame = frameGroups[type][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(curFrame>=frames)
|
||||
{
|
||||
curFrame = 0;
|
||||
}
|
||||
}
|
||||
currentFrame = 0;
|
||||
once = false;
|
||||
|
||||
play();
|
||||
}
|
||||
|
||||
CCreatureAnimation::CCreatureAnimation(std::string name) : internalFrame(0), once(false)
|
||||
CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController controller)
|
||||
: defName(name),
|
||||
speed(0.1),
|
||||
currentFrame(0),
|
||||
elapsedTime(0),
|
||||
type(CCreatureAnim::HOLDING),
|
||||
border({0, 0, 0, 0}),
|
||||
speedController(controller),
|
||||
once(false)
|
||||
{
|
||||
//load main file
|
||||
FDef = CResourceHandler::get()->loadData(
|
||||
ResourceID(std::string("SPRITES/") + name, EResType::ANIMATION)).first.release();
|
||||
// separate block to avoid accidental use of "data" after it was moved into "pixelData"
|
||||
{
|
||||
auto data = CResourceHandler::get()->loadData(
|
||||
ResourceID(std::string("SPRITES/") + name, EResType::ANIMATION));
|
||||
|
||||
//init anim data
|
||||
int i,j, totalInBlock;
|
||||
pixelData = std::move(data.first);
|
||||
pixelDataSize = data.second;
|
||||
}
|
||||
|
||||
defName=name;
|
||||
i = 0;
|
||||
DEFType = read_le_u32(FDef + i); i+=4;
|
||||
fullWidth = read_le_u32(FDef + i); i+=4;
|
||||
fullHeight = read_le_u32(FDef + i); i+=4;
|
||||
i=0xc;
|
||||
totalBlocks = read_le_u32(FDef + i); i+=4;
|
||||
CBinaryReader reader(new CMemoryStream(pixelData.get(), pixelDataSize));
|
||||
|
||||
reader.readInt32(); // def type, unused
|
||||
|
||||
fullWidth = reader.readInt32();
|
||||
fullHeight = reader.readInt32();
|
||||
|
||||
int totalBlocks = reader.readInt32();
|
||||
|
||||
i=0x10;
|
||||
for (auto & elem : palette)
|
||||
{
|
||||
elem.R = FDef[i++];
|
||||
elem.G = FDef[i++];
|
||||
elem.B = FDef[i++];
|
||||
elem.F = 0;
|
||||
elem.r = reader.readUInt8();
|
||||
elem.g = reader.readUInt8();
|
||||
elem.b = reader.readUInt8();
|
||||
elem.unused = 0;
|
||||
}
|
||||
i=0x310;
|
||||
totalEntries=0;
|
||||
for (int z=0; z<totalBlocks; z++)
|
||||
|
||||
for (int i=0; i<totalBlocks; i++)
|
||||
{
|
||||
std::vector<int> frameIDs;
|
||||
int group = read_le_u32(FDef + i); i+=4; //block ID
|
||||
totalInBlock = read_le_u32(FDef + i); i+=4;
|
||||
for (j=SEntries.size(); j<totalEntries+totalInBlock; j++)
|
||||
{
|
||||
SEntries.push_back(SEntry());
|
||||
SEntries[j].group = group;
|
||||
frameIDs.push_back(j);
|
||||
}
|
||||
/*int unknown2 = read_le_u32(FDef + i);*/ i+=4; //TODO use me
|
||||
/*int unknown3 = read_le_u32(FDef + i);*/ i+=4; //TODO use me
|
||||
i+=13*totalInBlock; //omitting names
|
||||
for (j=0; j<totalInBlock; j++)
|
||||
{
|
||||
SEntries[totalEntries+j].offset = read_le_u32(FDef + i); i+=4;
|
||||
}
|
||||
//totalEntries+=totalInBlock;
|
||||
for(int hh=0; hh<totalInBlock; ++hh)
|
||||
{
|
||||
++totalEntries;
|
||||
}
|
||||
frameGroups[group] = frameIDs;
|
||||
int groupID = reader.readInt32();
|
||||
|
||||
int totalInBlock = reader.readInt32();
|
||||
|
||||
reader.skip(4 + 4 + 13 * totalInBlock); // some unused data
|
||||
|
||||
for (int j=0; j<totalInBlock; j++)
|
||||
dataOffsets[groupID].push_back(reader.readUInt32());
|
||||
}
|
||||
|
||||
//init vars
|
||||
curFrame = 0;
|
||||
type = CCreatureAnim::WHOLE_ANIM;
|
||||
frames = totalEntries;
|
||||
// if necessary, add one frame into vcmi-only group DEAD
|
||||
if (dataOffsets.count(CCreatureAnim::DEAD) == 0)
|
||||
dataOffsets[CCreatureAnim::DEAD].push_back(dataOffsets[CCreatureAnim::DEATH].back());
|
||||
|
||||
play();
|
||||
}
|
||||
|
||||
int CCreatureAnimation::nextFrameMiddle(SDL_Surface *dest, int x, int y, bool attacker, ui8 animCount, bool incrementFrame, bool yellowBorder, bool blueBorder, SDL_Rect * destRect)
|
||||
void CCreatureAnimation::endAnimation()
|
||||
{
|
||||
return nextFrame(dest, x-fullWidth/2, y-fullHeight/2, attacker, animCount, incrementFrame, yellowBorder, blueBorder, destRect);
|
||||
once = false;
|
||||
auto copy = onAnimationReset;
|
||||
onAnimationReset.clear();
|
||||
copy();
|
||||
}
|
||||
|
||||
void CCreatureAnimation::incrementFrame()
|
||||
bool CCreatureAnimation::incrementFrame(float timePassed)
|
||||
{
|
||||
if(type!=-1) //when a specific part of animation is played
|
||||
elapsedTime += timePassed;
|
||||
currentFrame += timePassed * speed;
|
||||
if (currentFrame >= float(framesInGroup(type)))
|
||||
{
|
||||
++internalFrame;
|
||||
if(internalFrame == frameGroups[type].size()) //rewind
|
||||
{
|
||||
internalFrame = 0;
|
||||
if(once) //playing animation once - return to standing animation
|
||||
{
|
||||
type = CCreatureAnim::HOLDING;
|
||||
once = false;
|
||||
curFrame = frameGroups[2][0];
|
||||
}
|
||||
else //
|
||||
{
|
||||
curFrame = frameGroups[type][0];
|
||||
}
|
||||
}
|
||||
curFrame = frameGroups[type][internalFrame];
|
||||
}
|
||||
else //when whole animation is played
|
||||
{
|
||||
++curFrame;
|
||||
if(curFrame>=frames)
|
||||
curFrame = 0;
|
||||
}
|
||||
}
|
||||
// just in case of extremely low fps
|
||||
while (currentFrame >= float(framesInGroup(type)))
|
||||
currentFrame -= framesInGroup(type);
|
||||
|
||||
int CCreatureAnimation::getFrame() const
|
||||
{
|
||||
return curFrame;
|
||||
}
|
||||
if (once)
|
||||
setType(CCreatureAnim::HOLDING);
|
||||
|
||||
int CCreatureAnimation::getAnimationFrame() const
|
||||
{
|
||||
return internalFrame;
|
||||
}
|
||||
|
||||
bool CCreatureAnimation::onFirstFrameInGroup()
|
||||
{
|
||||
return internalFrame == 0;
|
||||
}
|
||||
|
||||
bool CCreatureAnimation::onLastFrameInGroup()
|
||||
{
|
||||
if(internalFrame == frameGroups[type].size() - 1)
|
||||
endAnimation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCreatureAnimation::setBorderColor(SDL_Color palette)
|
||||
{
|
||||
border = palette;
|
||||
}
|
||||
|
||||
int CCreatureAnimation::getWidth() const
|
||||
{
|
||||
return fullWidth;
|
||||
}
|
||||
|
||||
int CCreatureAnimation::getHeight() const
|
||||
{
|
||||
return fullHeight;
|
||||
}
|
||||
|
||||
float CCreatureAnimation::getCurrentFrame() const
|
||||
{
|
||||
return currentFrame;
|
||||
}
|
||||
|
||||
void CCreatureAnimation::playOnce( CCreatureAnim::EAnimType type )
|
||||
{
|
||||
setType(type);
|
||||
once = true;
|
||||
}
|
||||
|
||||
|
||||
template<int bpp>
|
||||
int CCreatureAnimation::nextFrameT(SDL_Surface * dest, int x, int y, bool attacker, ui8 animCount, bool IncrementFrame /*= true*/, bool yellowBorder /*= false*/, bool blueBorder /*= false*/, SDL_Rect * destRect /*= nullptr*/)
|
||||
inline int getBorderStrength(float time)
|
||||
{
|
||||
//increasing frame number
|
||||
int SIndex = curFrame;
|
||||
if (IncrementFrame)
|
||||
incrementFrame();
|
||||
float borderStrength = fabs(round(time) - time) * 2; // generate value in range 0-1
|
||||
|
||||
#if 0
|
||||
long SpriteWidth, SpriteHeight, //sprite format
|
||||
LeftMargin, RightMargin, TopMargin,BottomMargin,
|
||||
i, FullHeight,
|
||||
|
||||
#endif
|
||||
ui8 SegmentType, SegmentLength;
|
||||
ui32 i;
|
||||
|
||||
i = SEntries[SIndex].offset;
|
||||
|
||||
/*int prSize = read_le_u32(FDef + i);*/ i += 4; //TODO use me
|
||||
const ui32 defType2 = read_le_u32(FDef + i); i += 4;
|
||||
const ui32 FullWidth = read_le_u32(FDef + i); i += 4;
|
||||
const ui32 FullHeight = read_le_u32(FDef + i); i += 4;
|
||||
const ui32 SpriteWidth = read_le_u32(FDef + i); i += 4;
|
||||
const ui32 SpriteHeight = read_le_u32(FDef + i); i += 4;
|
||||
const int LeftMargin = read_le_u32(FDef + i); i += 4;
|
||||
const int TopMargin = read_le_u32(FDef + i); i += 4;
|
||||
const int RightMargin = FullWidth - SpriteWidth - LeftMargin;
|
||||
const int BottomMargin = FullHeight - SpriteHeight - TopMargin;
|
||||
|
||||
if (defType2 == 1) //as it should be always in creature animations
|
||||
{
|
||||
const int BaseOffsetor = i;
|
||||
int ftcp = 0;
|
||||
|
||||
if (TopMargin > 0)
|
||||
{
|
||||
ftcp += FullWidth * TopMargin;
|
||||
}
|
||||
ui32 *RLEntries = (ui32 *)(FDef + BaseOffsetor);
|
||||
|
||||
for (int i = 0; i < SpriteHeight; i++)
|
||||
{
|
||||
int BaseOffset = BaseOffsetor + read_le_u32(RLEntries + i);
|
||||
int TotalRowLength; // length of read segment
|
||||
|
||||
if (LeftMargin > 0)
|
||||
{
|
||||
ftcp += LeftMargin;
|
||||
}
|
||||
|
||||
TotalRowLength = 0;
|
||||
|
||||
// Note: Bug fixed (Rev 2115): The implementation of omitting lines was false.
|
||||
// We've to calculate several things so not showing/putting pixels should suffice.
|
||||
|
||||
int yB = ftcp / FullWidth + y;
|
||||
|
||||
do
|
||||
{
|
||||
SegmentType = FDef[BaseOffset++];
|
||||
SegmentLength = FDef[BaseOffset++];
|
||||
|
||||
const int remainder = ftcp % FullWidth;
|
||||
int xB = (attacker ? remainder : FullWidth - remainder - 1) + x;
|
||||
|
||||
const ui8 aCountMod = (animCount & 0x20) ? ((animCount & 0x1e) >> 1) << 4 : (0x0f - ((animCount & 0x1e) >> 1)) << 4;
|
||||
|
||||
for (int k = 0; k <= SegmentLength; k++)
|
||||
{
|
||||
if(xB >= 0 && xB < dest->w && yB >= 0 && yB < dest->h)
|
||||
{
|
||||
if(!destRect || (destRect->x <= xB && destRect->x + destRect->w > xB && destRect->y <= yB && destRect->y + destRect->h > yB))
|
||||
{
|
||||
const ui8 colorNr = SegmentType == 0xff ? FDef[BaseOffset+k] : SegmentType;
|
||||
putPixel<bpp>(dest, xB, yB, palette[colorNr], colorNr, yellowBorder, blueBorder, aCountMod);
|
||||
}
|
||||
}
|
||||
ftcp++; //increment pos
|
||||
if(attacker)
|
||||
xB++;
|
||||
else
|
||||
xB--;
|
||||
if ( SegmentType == 0xFF && TotalRowLength+k+1 >= SpriteWidth )
|
||||
break;
|
||||
}
|
||||
if (SegmentType == 0xFF)
|
||||
{
|
||||
BaseOffset += SegmentLength+1;
|
||||
}
|
||||
|
||||
TotalRowLength+=SegmentLength+1;
|
||||
} while(TotalRowLength < SpriteWidth);
|
||||
if (RightMargin > 0)
|
||||
{
|
||||
ftcp += RightMargin;
|
||||
}
|
||||
}
|
||||
if (BottomMargin > 0)
|
||||
{
|
||||
ftcp += BottomMargin * FullWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return borderStrength * 155 + 100; // scale to 0-255
|
||||
}
|
||||
|
||||
int CCreatureAnimation::nextFrame(SDL_Surface *dest, int x, int y, bool attacker, ui8 animCount, bool IncrementFrame, bool yellowBorder, bool blueBorder, SDL_Rect * destRect)
|
||||
static SDL_Color genShadow(ui8 alpha)
|
||||
{
|
||||
return {0, 0, 0, alpha};
|
||||
}
|
||||
|
||||
static SDL_Color genBorderColor(ui8 alpha, const SDL_Color & base)
|
||||
{
|
||||
return {base.r, base.g, base.b, ui8(base.unused * alpha / 256)};
|
||||
}
|
||||
|
||||
static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2)
|
||||
{
|
||||
return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256;
|
||||
}
|
||||
|
||||
static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
|
||||
{
|
||||
return {
|
||||
mixChannels(over.r, base.r, over.unused, base.unused),
|
||||
mixChannels(over.g, base.g, over.unused, base.unused),
|
||||
mixChannels(over.b, base.b, over.unused, base.unused),
|
||||
ui8(over.unused + base.unused * (255 - over.unused) / 256)
|
||||
};
|
||||
}
|
||||
|
||||
std::array<SDL_Color, 8> CCreatureAnimation::genSpecialPalette()
|
||||
{
|
||||
std::array<SDL_Color, 8> ret;
|
||||
|
||||
ret[0] = genShadow(0);
|
||||
ret[1] = genShadow(64);
|
||||
ret[2] = genShadow(128);
|
||||
ret[3] = genShadow(128);
|
||||
ret[4] = genShadow(128);
|
||||
ret[5] = genBorderColor(getBorderStrength(elapsedTime), border);
|
||||
ret[6] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
ret[7] = addColors(genShadow(64), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
void CCreatureAnimation::nextFrameT(SDL_Surface * dest, int x, int y, bool rotate, SDL_Rect * destRect /*= nullptr*/)
|
||||
{
|
||||
assert(dataOffsets.count(type) && dataOffsets.at(type).size() > size_t(currentFrame));
|
||||
|
||||
ui32 offset = dataOffsets.at(type).at(floor(currentFrame));
|
||||
|
||||
CBinaryReader reader(new CMemoryStream(pixelData.get(), pixelDataSize));
|
||||
|
||||
reader.getStream()->seek(offset);
|
||||
|
||||
reader.readUInt32(); // unused, size of pixel data for this frame
|
||||
const ui32 defType2 = reader.readUInt32();
|
||||
const ui32 fullWidth = reader.readUInt32();
|
||||
/*const ui32 fullHeight =*/ reader.readUInt32();
|
||||
const ui32 spriteWidth = reader.readUInt32();
|
||||
const ui32 spriteHeight = reader.readUInt32();
|
||||
const int leftMargin = reader.readInt32();
|
||||
const int topMargin = reader.readInt32();
|
||||
|
||||
const int rightMargin = fullWidth - spriteWidth - leftMargin;
|
||||
//const int bottomMargin = fullHeight - spriteHeight - topMargin;
|
||||
|
||||
const size_t baseOffset = reader.getStream()->tell();
|
||||
|
||||
assert(defType2 == 1);
|
||||
|
||||
auto specialPalette = genSpecialPalette();
|
||||
|
||||
for (ui32 i=0; i<spriteHeight; i++)
|
||||
{
|
||||
//NOTE: if this loop will be optimized to skip empty lines - recheck this read access
|
||||
ui8 * lineData = pixelData.get() + baseOffset + reader.readUInt32();
|
||||
|
||||
size_t destX = x;
|
||||
if (rotate)
|
||||
destX += rightMargin + spriteWidth - 1;
|
||||
else
|
||||
destX += leftMargin;
|
||||
|
||||
size_t destY = y + topMargin + i;
|
||||
size_t currentOffset = 0;
|
||||
size_t totalRowLength = 0;
|
||||
|
||||
while (totalRowLength < spriteWidth)
|
||||
{
|
||||
ui8 type = lineData[currentOffset++];
|
||||
ui32 length = lineData[currentOffset++] + 1;
|
||||
|
||||
if (type==0xFF)//Raw data
|
||||
{
|
||||
for (size_t j=0; j<length; j++)
|
||||
putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, lineData[currentOffset + j], specialPalette, destRect);
|
||||
|
||||
currentOffset += length;
|
||||
}
|
||||
else// RLE
|
||||
{
|
||||
if (type != 0) // transparency row, handle it here for speed
|
||||
{
|
||||
for (size_t j=0; j<length; j++)
|
||||
putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, type, specialPalette, destRect);
|
||||
}
|
||||
}
|
||||
|
||||
destX += rotate ? (-length) : (length);
|
||||
totalRowLength += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCreatureAnimation::nextFrame(SDL_Surface *dest, int x, int y, bool attacker, SDL_Rect * destRect)
|
||||
{
|
||||
switch(dest->format->BytesPerPixel)
|
||||
{
|
||||
case 2: return nextFrameT<2>(dest, x, y, attacker, animCount, IncrementFrame, yellowBorder, blueBorder, destRect);
|
||||
case 3: return nextFrameT<3>(dest, x, y, attacker, animCount, IncrementFrame, yellowBorder, blueBorder, destRect);
|
||||
case 4: return nextFrameT<4>(dest, x, y, attacker, animCount, IncrementFrame, yellowBorder, blueBorder, destRect);
|
||||
case 2: return nextFrameT<2>(dest, x, y, !attacker, destRect);
|
||||
case 3: return nextFrameT<3>(dest, x, y, !attacker, destRect);
|
||||
case 4: return nextFrameT<4>(dest, x, y, !attacker, destRect);
|
||||
default:
|
||||
logGlobal->errorStream() << (int)dest->format->BitsPerPixel << " bpp is not supported!!!";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int CCreatureAnimation::framesInGroup(CCreatureAnim::EAnimType group) const
|
||||
{
|
||||
if(frameGroups.find(group) == frameGroups.end())
|
||||
if(dataOffsets.count(group) == 0)
|
||||
return 0;
|
||||
return frameGroups.find(group)->second.size();
|
||||
|
||||
return dataOffsets.at(group).size();
|
||||
}
|
||||
|
||||
CCreatureAnimation::~CCreatureAnimation()
|
||||
ui8 * CCreatureAnimation::getPixelAddr(SDL_Surface * dest, int X, int Y) const
|
||||
{
|
||||
delete [] FDef;
|
||||
return (ui8*)dest->pixels + X * dest->format->BytesPerPixel + Y * dest->pitch;
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
inline void CCreatureAnimation::putPixel(
|
||||
SDL_Surface * dest,
|
||||
const int & ftcpX,
|
||||
const int & ftcpY,
|
||||
const BMPPalette & color,
|
||||
const ui8 & palc,
|
||||
const bool & yellowBorder,
|
||||
const bool & blueBorder,
|
||||
const ui8 & animCount
|
||||
) const
|
||||
{
|
||||
if(palc!=0)
|
||||
inline void CCreatureAnimation::putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special, SDL_Rect * destRect) const
|
||||
{
|
||||
if (destRect == nullptr)
|
||||
putPixel<bpp>(getPixelAddr(dest, X, Y), palette[index], index, special);
|
||||
else
|
||||
{
|
||||
Uint8 * p = (Uint8*)dest->pixels + ftcpX*dest->format->BytesPerPixel + ftcpY*dest->pitch;
|
||||
if(palc > 7) //normal color
|
||||
{
|
||||
ColorPutter<bpp, 0>::PutColor(p, color.R, color.G, color.B);
|
||||
}
|
||||
else if((yellowBorder || blueBorder) && (palc == 6 || palc == 7)) //selection highlight
|
||||
{
|
||||
if(blueBorder)
|
||||
ColorPutter<bpp, 0>::PutColor(p, 0, 0x0f + animCount, 0x0f + animCount);
|
||||
else
|
||||
ColorPutter<bpp, 0>::PutColor(p, 0x0f + animCount, 0x0f + animCount, 0);
|
||||
}
|
||||
else if (palc == 5) //selection highlight or transparent
|
||||
{
|
||||
if(blueBorder)
|
||||
ColorPutter<bpp, 0>::PutColor(p, color.B, color.G - 0xf0 + animCount, color.R - 0xf0 + animCount); //shouldn't it be reversed? its bgr instead of rgb
|
||||
else if (yellowBorder)
|
||||
ColorPutter<bpp, 0>::PutColor(p, color.R - 0xf0 + animCount, color.G - 0xf0 + animCount, color.B);
|
||||
}
|
||||
else //shadow
|
||||
{
|
||||
//determining transparency value, 255 or 0 should be already filtered
|
||||
static Uint16 colToAlpha[8] = {255,192,128,128,128,255,128,192};
|
||||
Uint16 alpha = colToAlpha[palc];
|
||||
|
||||
if(bpp != 3 && bpp != 4)
|
||||
{
|
||||
ColorPutter<bpp, 0>::PutColor(p, 0, 0, 0, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
p[0] = (p[0] * alpha)>>8;
|
||||
p[1] = (p[1] * alpha)>>8;
|
||||
p[2] = (p[2] * alpha)>>8;
|
||||
}
|
||||
}
|
||||
if ( X > destRect->x && X < destRect->w + destRect->x &&
|
||||
Y > destRect->y && Y < destRect->h + destRect->y )
|
||||
putPixel<bpp>(getPixelAddr(dest, X, Y), palette[index], index, special);
|
||||
}
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
inline void CCreatureAnimation::putPixel(ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const
|
||||
{
|
||||
if (index < 8)
|
||||
{
|
||||
const SDL_Color & pal = special[index];
|
||||
ColorPutter<bpp, 0>::PutColor(dest, pal.r, pal.g, pal.b, pal.unused);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorPutter<bpp, 0>::PutColor(dest, color.r, color.g, color.b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CCreatureAnimation::isDead() const
|
||||
{
|
||||
return getType() == CCreatureAnim::DEAD
|
||||
|| getType() == CCreatureAnim::DEATH;
|
||||
}
|
||||
|
||||
bool CCreatureAnimation::isIdle() const
|
||||
{
|
||||
return getType() == CCreatureAnim::HOLDING
|
||||
|| getType() == CCreatureAnim::MOUSEON;
|
||||
}
|
||||
|
||||
void CCreatureAnimation::pause()
|
||||
{
|
||||
speed = 0;
|
||||
}
|
||||
|
||||
void CCreatureAnimation::play()
|
||||
{
|
||||
speed = speedController(this, type);
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../CDefHandler.h"
|
||||
#include "../../client/CBitmapHandler.h"
|
||||
#include "../FunctionList.h"
|
||||
//#include "../CDefHandler.h"
|
||||
#include "../CAnimation.h"
|
||||
|
||||
/*
|
||||
@ -15,65 +14,124 @@
|
||||
*
|
||||
*/
|
||||
|
||||
struct BMPPalette;
|
||||
class CIntObject;
|
||||
class CCreatureAnimation;
|
||||
|
||||
/// Namespace for some common controls of animations
|
||||
namespace AnimationControls
|
||||
{
|
||||
/// get SDL_Color for creature selection borders
|
||||
SDL_Color getBlueBorder();
|
||||
SDL_Color getGoldBorder();
|
||||
SDL_Color getNoBorder();
|
||||
|
||||
/// creates animation object with preset speed control
|
||||
CCreatureAnimation * getAnimation(const CCreature * creature);
|
||||
|
||||
/// returns animation speed of specific group, taking in mind game setting
|
||||
float getCreatureAnimationSpeed(const CCreature * creature, const CCreatureAnimation * anim, size_t groupID);
|
||||
|
||||
/// returns how far projectile should move each frame
|
||||
/// TODO: make it time-based
|
||||
float getProjectileSpeed();
|
||||
|
||||
/// returns duration of full movement animation, in seconds. Needed to move animation on screen
|
||||
float getMovementDuration(const CCreature * creature);
|
||||
|
||||
/// Returns distance on which flying creatures should during one animation loop
|
||||
float getFlightDistance(const CCreature * creature);
|
||||
}
|
||||
|
||||
/// Class which manages animations of creatures/units inside battles
|
||||
/// TODO: split into constant image container and class that does *control* of animation
|
||||
class CCreatureAnimation : public CIntObject
|
||||
{
|
||||
private:
|
||||
int totalEntries, DEFType, totalBlocks;
|
||||
BMPPalette palette[256];
|
||||
struct SEntry
|
||||
{
|
||||
int offset;
|
||||
int group;
|
||||
} ;
|
||||
std::vector<SEntry> SEntries ;
|
||||
std::string defName, curDir;
|
||||
|
||||
template<int bpp>
|
||||
void putPixel(
|
||||
SDL_Surface * dest,
|
||||
const int & ftcpX,
|
||||
const int & ftcpY,
|
||||
const BMPPalette & color,
|
||||
const ui8 & palc,
|
||||
const bool & yellowBorder,
|
||||
const bool & blueBorder,
|
||||
const ui8 & animCount
|
||||
) const;
|
||||
|
||||
////////////
|
||||
|
||||
ui8 * FDef; //animation raw data
|
||||
int curFrame, internalFrame; //number of currently displayed frame
|
||||
ui32 frames; //number of frames
|
||||
CCreatureAnim::EAnimType type; //type of animation being displayed (-1 - whole animation, >0 - specified part [default: -1])
|
||||
|
||||
template<int bpp>
|
||||
int nextFrameT(SDL_Surface * dest, int x, int y, bool attacker, ui8 animCount, bool incrementFrame = true, bool yellowBorder = false, bool blueBorder = false, SDL_Rect * destRect = nullptr); //0 - success, any other - error //print next
|
||||
int nextFrameMiddle(SDL_Surface * dest, int x, int y, bool attacker, ui8 animCount, bool IncrementFrame = true, bool yellowBorder = false, bool blueBorder = false, SDL_Rect * destRect = nullptr); //0 - success, any other - error //print next
|
||||
|
||||
std::map<int, std::vector<int> > frameGroups; //groups of frames; [groupID] -> vector of frame IDs in group
|
||||
bool once;
|
||||
|
||||
public:
|
||||
int fullWidth, fullHeight; //read-only, please!
|
||||
CCreatureAnimation(std::string name); //c-tor
|
||||
~CCreatureAnimation(); //d-tor
|
||||
typedef boost::function<float(CCreatureAnimation *, size_t)> TSpeedController;
|
||||
|
||||
private:
|
||||
std::string defName;
|
||||
|
||||
int fullWidth, fullHeight;
|
||||
|
||||
// palette, as read from def file
|
||||
std::array<SDL_Color, 256> palette;
|
||||
|
||||
//key = id of group (note that some groups may be missing)
|
||||
//value = offset of pixel data for each frame, vector size = number of frames in group
|
||||
std::map<int, std::vector<unsigned int>> dataOffsets;
|
||||
|
||||
//animation raw data
|
||||
//TODO: use vector instead?
|
||||
unique_ptr<ui8[]> pixelData;
|
||||
size_t pixelDataSize;
|
||||
|
||||
// speed of animation, measure in frames per second
|
||||
float speed;
|
||||
|
||||
// currently displayed frame. Float to allow H3-style animations where frames
|
||||
// don't display for integer number of frames
|
||||
float currentFrame;
|
||||
// cumulative, real-time duration of animation. Used for effects like selection border
|
||||
float elapsedTime;
|
||||
CCreatureAnim::EAnimType type; //type of animation being displayed
|
||||
|
||||
// border color, disabled if alpha = 0
|
||||
SDL_Color border;
|
||||
|
||||
TSpeedController speedController;
|
||||
|
||||
bool once; // animation will be played once and the reset to idling
|
||||
|
||||
ui8 * getPixelAddr(SDL_Surface * dest, int ftcpX, int ftcpY) const;
|
||||
|
||||
template<int bpp>
|
||||
void putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special, SDL_Rect * destRect = nullptr) const;
|
||||
|
||||
template<int bpp>
|
||||
void putPixel( ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const;
|
||||
|
||||
template<int bpp>
|
||||
void nextFrameT(SDL_Surface * dest, int x, int y, bool rotate, SDL_Rect * destRect = nullptr);
|
||||
|
||||
void endAnimation();
|
||||
|
||||
/// creates 8 special colors for current frame
|
||||
std::array<SDL_Color, 8> genSpecialPalette();
|
||||
public:
|
||||
|
||||
// function(s) that will be called when animation ends, after reset to 1st frame
|
||||
// NOTE that these function will be fired only once
|
||||
CFunctionList<void()> onAnimationReset;
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
/// Constructor
|
||||
/// name - path to .def file, relative to SPRITES/ directory
|
||||
/// controller - function that will return for how long *each* frame
|
||||
/// in specified group of animation should be played, measured in seconds
|
||||
CCreatureAnimation(std::string name, TSpeedController speedController); //c-tor
|
||||
|
||||
void setType(CCreatureAnim::EAnimType type); //sets type of animation and cleares framecount
|
||||
CCreatureAnim::EAnimType getType() const; //returns type of animation
|
||||
|
||||
int nextFrame(SDL_Surface * dest, int x, int y, bool attacker, ui8 animCount, bool incrementFrame = true, bool yellowBorder = false, bool blueBorder = false, SDL_Rect * destRect = nullptr); //0 - success, any other - error //print next
|
||||
void incrementFrame();
|
||||
int getFrame() const; // Gets the current frame ID relative to DEF file.
|
||||
int getAnimationFrame() const; // Gets the current frame ID relative to frame group.
|
||||
bool onFirstFrameInGroup();
|
||||
bool onLastFrameInGroup();
|
||||
void nextFrame(SDL_Surface * dest, int x, int y, bool rotate, SDL_Rect * destRect = nullptr);
|
||||
|
||||
// should be called every frame, return true when animation was reset to beginning
|
||||
bool incrementFrame(float timePassed);
|
||||
void setBorderColor(SDL_Color palette);
|
||||
|
||||
float getCurrentFrame() const; // Gets the current frame ID relative to frame group.
|
||||
|
||||
void playOnce(CCreatureAnim::EAnimType type); //plays once given stage of animation, then resets to 2
|
||||
|
||||
int framesInGroup(CCreatureAnim::EAnimType group) const; //retirns number of fromes in given group
|
||||
|
||||
void pause();
|
||||
void play();
|
||||
|
||||
//helpers. TODO: move them somewhere else
|
||||
bool isDead() const;
|
||||
bool isIdle() const;
|
||||
};
|
||||
|
@ -546,12 +546,14 @@ CHighlightableButtonsGroup::~CHighlightableButtonsGroup()
|
||||
|
||||
void CHighlightableButtonsGroup::select(int id, bool mode)
|
||||
{
|
||||
CHighlightableButton *bt = nullptr;
|
||||
assert(!buttons.empty());
|
||||
|
||||
CHighlightableButton *bt = buttons.front();
|
||||
if(mode)
|
||||
{
|
||||
for(size_t i=0;i<buttons.size() && !bt; ++i)
|
||||
if (buttons[i]->ID == id)
|
||||
bt = buttons[i];
|
||||
for(auto btn : buttons)
|
||||
if (btn->ID == id)
|
||||
bt = btn;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -62,16 +62,6 @@ void blitAt(SDL_Surface * src, const SDL_Rect & pos, SDL_Surface * dst)
|
||||
blitAt(src,pos.x,pos.y,dst);
|
||||
}
|
||||
|
||||
SDL_Color genRGB(int r, int g, int b, int a=0)
|
||||
{
|
||||
SDL_Color ret;
|
||||
ret.b=b;
|
||||
ret.g=g;
|
||||
ret.r=r;
|
||||
ret.unused=a;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void updateRect (SDL_Rect * rect, SDL_Surface * scr)
|
||||
{
|
||||
SDL_UpdateRect(scr,rect->x,rect->y,rect->w,rect->h);
|
||||
|
@ -60,12 +60,14 @@ std::vector<BattleHex> BattleHex::neighbouringTiles() const
|
||||
{
|
||||
std::vector<BattleHex> ret;
|
||||
const int WN = GameConstants::BFIELD_WIDTH;
|
||||
checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), ret);
|
||||
checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), ret);
|
||||
checkAndPush(hex - 1, ret);
|
||||
checkAndPush(hex + 1, ret);
|
||||
checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), ret);
|
||||
checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), ret);
|
||||
// H3 order : TR, R, BR, BL, L, TL (T = top, B = bottom ...)
|
||||
|
||||
checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), ret); // 1
|
||||
checkAndPush(hex + 1, ret); // 2
|
||||
checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), ret); // 3
|
||||
checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), ret); // 4
|
||||
checkAndPush(hex - 1, ret); // 5
|
||||
checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), ret); // 6
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -579,9 +579,12 @@ CCreature * CCreatureHandler::loadFromJson(const JsonNode & node)
|
||||
cre->addBonus(node["speed"].Float(), Bonus::STACKS_SPEED);
|
||||
cre->addBonus(node["attack"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
cre->addBonus(node["defense"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
|
||||
cre->addBonus(node["damage"]["min"].Float(), Bonus::CREATURE_DAMAGE, 1);
|
||||
cre->addBonus(node["damage"]["max"].Float(), Bonus::CREATURE_DAMAGE, 2);
|
||||
|
||||
assert(node["damage"]["min"].Float() <= node["damage"]["max"].Float());
|
||||
|
||||
cre->ammMin = node["advMapAmount"]["min"].Float();
|
||||
cre->ammMax = node["advMapAmount"]["max"].Float();
|
||||
assert(cre->ammMin <= cre->ammMax);
|
||||
|
@ -3468,8 +3468,14 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
||||
handleAfterAttackCasting(bat);
|
||||
}
|
||||
|
||||
//ballista & artillery handling
|
||||
if(destStack->alive() && stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
//second shot for ballista, only if hero has advanced artillery
|
||||
|
||||
const CGHeroInstance * attackingHero = gs->curB->heroes[ba.side];
|
||||
|
||||
if( destStack->alive()
|
||||
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
||||
)
|
||||
{
|
||||
BattleAttack bat2;
|
||||
bat2.flags |= BattleAttack::SHOT;
|
||||
|
@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
CRandomGenerator gen;
|
||||
const JsonNode viewNode(ResourceID("test/terrainViewMappings", EResType::TEXT));
|
||||
const auto & mappingsNode = viewNode["mappings"].Vector();
|
||||
BOOST_FOREACH(const auto & node, mappingsNode)
|
||||
for (const auto & node : mappingsNode)
|
||||
{
|
||||
// Get terrain group and id
|
||||
const auto & patternStr = node["pattern"].String();
|
||||
@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
const auto & mapping = (*pattern).mapping;
|
||||
|
||||
const auto & positionsNode = node["pos"].Vector();
|
||||
BOOST_FOREACH(const auto & posNode, positionsNode)
|
||||
for (const auto & posNode : positionsNode)
|
||||
{
|
||||
const auto & posVector = posNode.Vector();
|
||||
if(posVector.size() != 3) throw std::runtime_error("A position should consist of three values x,y,z. Continue with next position.");
|
||||
@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
editManager->drawTerrain(originalTile.terType, &gen);
|
||||
const auto & tile = map->getTile(pos);
|
||||
bool isInRange = false;
|
||||
BOOST_FOREACH(const auto & range, mapping)
|
||||
for(const auto & range : mapping)
|
||||
{
|
||||
if(tile.terView >= range.first && tile.terView <= range.second)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user