1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #1515 from IvanSavenko/time_based_animations

Time based animations
This commit is contained in:
Ivan Savenko 2023-02-01 12:25:54 +02:00 committed by GitHub
commit 0ba74fea73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 172 additions and 149 deletions

View File

@ -21,6 +21,14 @@
extern CGuiHandler GH; //global gui handler
#ifndef DISABLE_VIDEO
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
//reads events and returns true on key down
static bool keyDown()
{
@ -59,22 +67,20 @@ static si64 lodSeek(void * opaque, si64 pos, int whence)
}
CVideoPlayer::CVideoPlayer()
{
stream = -1;
format = nullptr;
codecContext = nullptr;
codec = nullptr;
frame = nullptr;
sws = nullptr;
context = nullptr;
texture = nullptr;
dest = nullptr;
destRect = CSDL_Ext::genRect(0,0,0,0);
pos = CSDL_Ext::genRect(0,0,0,0);
refreshWait = 0;
refreshCount = 0;
doLoop = false;
}
: stream(-1)
, format (nullptr)
, codecContext(nullptr)
, codec(nullptr)
, frame(nullptr)
, sws(nullptr)
, context(nullptr)
, texture(nullptr)
, dest(nullptr)
, destRect(0,0,0,0)
, pos(0,0,0,0)
, frameTime(0)
, doLoop(false)
{}
bool CVideoPlayer::open(std::string fname, bool scale)
{
@ -88,9 +94,8 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
close();
this->fname = fname;
refreshWait = 3;
refreshCount = -1;
doLoop = loop;
frameTime = 0;
ResourceID resource(std::string("Video/") + fname, EResType::VIDEO);
@ -257,6 +262,7 @@ bool CVideoPlayer::nextFrame()
if (doLoop && !gotError)
{
// Rewind
frameTime = 0;
if (av_seek_frame(format, stream, 0, AVSEEK_FLAG_BYTE) < 0)
break;
gotError = true;
@ -357,9 +363,11 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
if (sws == nullptr)
return;
if (refreshCount <= 0)
double frameEndTime = (frame->pts + frame->pkt_duration) * av_q2d(format->streams[stream]->time_base);
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.0;
if (frameTime >= frameEndTime )
{
refreshCount = refreshWait;
if (nextFrame())
show(x,y,dst,update);
else
@ -377,8 +385,6 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
{
redraw(x, y, dst, update);
}
refreshCount --;
}
void CVideoPlayer::close()

View File

@ -56,39 +56,11 @@ public:
#include "../lib/filesystem/CInputStream.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
//compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3
#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100))
inline AVFrame * av_frame_alloc()
{
return avcodec_alloc_frame();
}
inline void av_frame_free(AVFrame ** frame)
{
av_free(*frame);
*frame = nullptr;
}
#endif // VCMI_USE_OLD_AVUTIL
//fix for travis-ci
#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 0, 0))
#define AVPixelFormat PixelFormat
#define AV_PIX_FMT_NONE PIX_FMT_NONE
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
#endif
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFrame;
struct AVIOContext;
class CVideoPlayer : public IMainVideoPlayer
{
@ -108,8 +80,8 @@ class CVideoPlayer : public IMainVideoPlayer
Rect destRect; // valid when dest is used
Rect pos; // destination on screen
int refreshWait; // Wait several refresh before updating the image
int refreshCount;
/// video playback currnet progress, in seconds
double frameTime;
bool doLoop; // loop through video
bool playVideo(int x, int y, bool stopOnKey);
@ -141,4 +113,3 @@ public:
};
#endif

View File

@ -364,7 +364,7 @@ bool MovementAnimation::init()
Point begPosition = owner.stacksController->getStackPositionAtHex(prevHex, stack);
Point endPosition = owner.stacksController->getStackPositionAtHex(nextHex, stack);
timeToMove = AnimationControls::getMovementDuration(stack->getCreature());
progressPerSecond = AnimationControls::getMovementDistance(stack->getCreature());
begX = begPosition.x;
begY = begPosition.y;
@ -375,8 +375,7 @@ bool MovementAnimation::init()
if (stack->hasBonus(Selector::type()(Bonus::FLYING)))
{
float distance = static_cast<float>(sqrt(distanceX * distanceX + distanceY * distanceY));
timeToMove *= AnimationControls::getFlightDistance(stack->getCreature()) / distance;
progressPerSecond = AnimationControls::getFlightDistance(stack->getCreature()) / distance;
}
return true;
@ -384,7 +383,7 @@ bool MovementAnimation::init()
void MovementAnimation::nextFrame()
{
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * timeToMove;
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * progressPerSecond;
//moving instructions
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
@ -432,7 +431,7 @@ MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stac
curentMoveIndex(0),
begX(0), begY(0),
distanceX(0), distanceY(0),
timeToMove(0.0),
progressPerSecond(0.0),
progress(0.0)
{
logAnim->debug("Created MovementAnimation for %s", stack->getName());

View File

@ -147,8 +147,11 @@ private:
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
/// progress gain per second
double progressPerSecond;
/// range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
double progress;
public:
bool init() override;

View File

@ -520,16 +520,16 @@ void BattleInterface::displaySpellHit(const CSpell * spell, BattleHex destinatio
void BattleInterface::setAnimSpeed(int set)
{
Settings speed = settings.write["battle"]["animationSpeed"];
speed->Float() = float(set) / 100;
Settings speed = settings.write["battle"]["speedFactor"];
speed->Float() = float(set);
}
int BattleInterface::getAnimSpeed() const
{
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
return static_cast<int>(vstd::round(settings["session"]["spectate-battle-speed"].Float() *100));
return static_cast<int>(vstd::round(settings["session"]["spectate-battle-speed"].Float()));
return static_cast<int>(vstd::round(settings["battle"]["animationSpeed"].Float() *100));
return static_cast<int>(vstd::round(settings["battle"]["speedFactor"].Float()));
}
CPlayerInterface *BattleInterface::getCurrentPlayerInterface() const

View File

@ -221,8 +221,10 @@ void BattleHero::render(Canvas & canvas)
canvas.draw(flagFrame, flagPosition);
canvas.draw(heroFrame, heroPosition);
flagCurrentFrame += currentSpeed;
currentFrame += currentSpeed;
float timePassed = float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
@ -241,8 +243,8 @@ void BattleHero::pause()
void BattleHero::play()
{
//FIXME: un-hardcode speed
currentSpeed = 0.25f;
//H3 speed: 10 fps ( 100 ms per frame)
currentSpeed = 10.f;
}
float BattleHero::getFrame() const
@ -441,13 +443,13 @@ BattleOptionsWindow::BattleOptionsWindow(BattleInterface & owner):
std::shared_ptr<CToggleButton> toggle;
toggle = std::make_shared<CToggleButton>(Point( 28, 225), "sysopb9.def", CGI->generaltexth->zelp[422]);
animSpeeds->addToggle(40, toggle);
animSpeeds->addToggle(1, toggle);
toggle = std::make_shared<CToggleButton>(Point( 92, 225), "sysob10.def", CGI->generaltexth->zelp[423]);
animSpeeds->addToggle(63, toggle);
animSpeeds->addToggle(2, toggle);
toggle = std::make_shared<CToggleButton>(Point(156, 225), "sysob11.def", CGI->generaltexth->zelp[424]);
animSpeeds->addToggle(100, toggle);
animSpeeds->addToggle(3, toggle);
animSpeeds->setSelected(owner.getAnimSpeed());

View File

@ -77,7 +77,11 @@ void ProjectileAnimatedMissile::show(Canvas & canvas)
void ProjectileCatapult::show(Canvas & canvas)
{
auto image = animation->getImage(frameNum, 0, true);
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0);
auto image = animation->getImage(frameIndex, 0, true);
if(image)
{
@ -86,8 +90,6 @@ void ProjectileCatapult::show(Canvas & canvas)
Point pos(posX, posY);
canvas.draw(image, pos);
frameNum = (frameNum + 1) % animation->size(0);
}
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
@ -289,13 +291,13 @@ void BattleProjectileController::createCatapultProjectile(const CStack * shooter
auto catapultProjectile = new ProjectileCatapult();
catapultProjectile->animation = getProjectileImage(shooter);
catapultProjectile->frameNum = 0;
catapultProjectile->progress = 0;
catapultProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getCatapultSpeed());
catapultProjectile->from = from;
catapultProjectile->dest = dest;
catapultProjectile->shooterID = shooter->ID;
catapultProjectile->playing = false;
catapultProjectile->frameProgress = 0.f;
projectiles.push_back(std::shared_ptr<ProjectileBase>(catapultProjectile));
}
@ -316,6 +318,7 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
projectile.reset(rayProjectile);
rayProjectile->rayConfig = shooterInfo.animation.projectileRay;
rayProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getRayProjectileSpeed());
}
else if (stackUsesMissileProjectile(shooter))
{
@ -323,11 +326,12 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
projectile.reset(missileProjectile);
missileProjectile->animation = getProjectileImage(shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
}
projectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter->ID;

View File

@ -61,7 +61,7 @@ struct ProjectileCatapult : ProjectileBase
void show(Canvas & canvas) override;
std::shared_ptr<CAnimation> animation;
int frameNum; // frame to display from projectile animation
float frameProgress;
};
/// Projectile for mages/evil eye - render ray expanding from origin position to destination

View File

@ -47,6 +47,15 @@ std::shared_ptr<CreatureAnimation> AnimationControls::getAnimation(const CCreatu
return std::make_shared<CreatureAnimation>(creature->animDefName, func);
}
float AnimationControls::getAnimationSpeedFactor()
{
// according to testing, H3 ratios between slow/medium/fast might actually be 36/60/100 (x1.666)
// exact value is hard to tell due to large rounding errors
// however we will assume them to be 33/66/100 since these values are better for standard 60 fps displays:
// with these numbers, base frame display duration will be 100/66/33 ms - exactly 6/4/2 frames
return settings["battle"]["speedFactor"].Float();
}
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType type)
{
assert(creature->animation.walkAnimationTime != 0);
@ -56,20 +65,23 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
// possible new fields for creature format:
//split "Attack time" into "Shoot Time" and "Cast Time"
// a lot of arbitrary multipliers, mostly to make animation speed closer to H3
const float baseSpeed = 0.1f;
const float speedMult = static_cast<float>(settings["battle"]["animationSpeed"].Float());
const float speed = baseSpeed / speedMult;
// base speed for all H3 animations on slow speed is 10 frames per second (or 100ms per frame)
const float baseSpeed = 10.f;
const float speed = baseSpeed * getAnimationSpeedFactor();
switch (type)
{
case ECreatureAnimType::MOVING:
return static_cast<float>(speed * 2 * creature->animation.walkAnimationTime / anim->framesInGroup(type));
return speed / creature->animation.walkAnimationTime;
case ECreatureAnimType::MOUSEON:
return baseSpeed;
case ECreatureAnimType::HOLDING:
return static_cast<float>(baseSpeed * creature->animation.idleAnimationTime / anim->framesInGroup(type));
if ( creature->animation.idleAnimationTime > 0.01)
return speed / creature->animation.idleAnimationTime;
else
return 0.f; // this animation is disabled for current creature
case ECreatureAnimType::SHOOT_UP:
case ECreatureAnimType::SHOOT_FRONT:
@ -80,7 +92,7 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::CAST_DOWN:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::CAST_UP:
return static_cast<float>(speed * 4 * creature->animation.attackAnimationTime / anim->framesInGroup(type));
return speed / creature->animation.attackAnimationTime;
// as strange as it looks like "attackAnimationTime" does not affects melee attacks
// necessary because length of these animations must be same for all creatures for synchronization
@ -95,15 +107,15 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::GROUP_ATTACK_DOWN:
case ECreatureAnimType::GROUP_ATTACK_FRONT:
case ECreatureAnimType::GROUP_ATTACK_UP:
return speed * 3 / anim->framesInGroup(type);
return speed;
case ECreatureAnimType::TURN_L:
case ECreatureAnimType::TURN_R:
return speed / 3;
return speed;
case ECreatureAnimType::MOVE_START:
case ECreatureAnimType::MOVE_END:
return speed / 3;
return speed;
case ECreatureAnimType::DEAD:
case ECreatureAnimType::DEAD_RANGED:
@ -116,37 +128,51 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
float AnimationControls::getProjectileSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 4000);
// H3 speed: 1250/2500/3750 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 1250);
}
float AnimationControls::getRayProjectileSpeed()
{
// H3 speed: 4000/8000/12000 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 4000);
}
float AnimationControls::getCatapultSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 1000);
// H3 speed: 200/400/600 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 200);
}
float AnimationControls::getSpellEffectSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 30);
// H3 speed: 10/20/30 frames per second
return static_cast<float>(getAnimationSpeedFactor() * 10);
}
float AnimationControls::getMovementDuration(const CCreature * creature)
float AnimationControls::getMovementDistance(const CCreature * creature)
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 4 / creature->animation.walkAnimationTime);
// H3 speed: 2/4/6 tiles per second
return static_cast<float>( 2.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFlightDistance(const CCreature * creature)
{
return static_cast<float>(creature->animation.flightAnimationDistance * 200);
// Note: for whatever reason, H3 uses "Walk Animation Time" here, even though "Flight Animation Distance" also exists
// H3 speed: 250/500/750 pixels per second
return static_cast<float>( 250.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFadeInDuration()
{
return 1.0f / settings["battle"]["animationSpeed"].Float();
// H3 speed: 500/250/166 ms
return 0.5f / getAnimationSpeedFactor();
}
float AnimationControls::getObstaclesSpeed()
{
return 10.0;// does not seems to be affected by animaiton speed settings
// H3 speed: 20 frames per second, irregardless of speed setting.
return 20.f;
}
ECreatureAnimType CreatureAnimation::getType() const
@ -407,7 +433,5 @@ void CreatureAnimation::pause()
void CreatureAnimation::play()
{
//logAnim->trace("Play %s group %d at %d:%d", name, static_cast<int>(getType()), pos.x, pos.y);
speed = 0;
if(speedController(this, type) != 0)
speed = 1 / speedController(this, type);
speed = speedController(this, type);
}

View File

@ -27,25 +27,32 @@ namespace AnimationControls
SDL_Color getGoldBorder();
SDL_Color getNoBorder();
/// returns animation speed factor according to game settings,
/// slow speed is considered to be "base speed" and will return 1.0
float getAnimationSpeedFactor();
/// creates animation object with preset speed control
std::shared_ptr<CreatureAnimation> getAnimation(const CCreature * creature);
/// returns animation speed of specific group, taking in mind game setting (in frames per second)
float getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType groupID);
/// returns how far projectile should move per second
/// returns how far projectile should move per second, in pixels per second
float getProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second (horizontal axis only)
/// returns how far projectile should move per second, in pixels per second
float getRayProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second, on a straight line, without parabola correction
float getCatapultSpeed();
/// returns speed of any spell effects, including any special effects like morale (in frames per second)
float getSpellEffectSpeed();
/// returns duration of full movement animation, in seconds. Needed to move animation on screen
float getMovementDuration(const CCreature * creature);
/// returns speed of movement animation across the screen, in tiles per second
float getMovementDistance(const CCreature * creature);
/// Returns distance on which flying creatures should during one animation loop
/// returns speed of movement animation across the screen, in pixels per seconds
float getFlightDistance(const CCreature * creature);
/// Returns total time for full fade-in effect on newly summoned creatures, in seconds

View File

@ -263,7 +263,7 @@ void CursorHandler::centerCursor()
void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f;
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;

View File

@ -680,7 +680,7 @@ CInfoBar::VisibleDateInfo::VisibleDateInfo()
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
animation = std::make_shared<CShowableAnim>(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE);
animation = std::make_shared<CShowableAnim>(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE, 180);// H3 uses around 175-180 ms per frame
std::string labelText;
if(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) == 1 && LOCPLINT->cb->getDate(Date::DAY) != 1) // monday of any week but first - show new week info
@ -721,8 +721,8 @@ CInfoBar::VisibleEnemyTurnInfo::VisibleEnemyTurnInfo(PlayerColor player)
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATNX");
banner = std::make_shared<CAnimImage>("CREST58", player.getNum(), 0, 20, 51);
sand = std::make_shared<CShowableAnim>(99, 51, "HOURSAND");
glass = std::make_shared<CShowableAnim>(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 40);
sand = std::make_shared<CShowableAnim>(99, 51, "HOURSAND", 0, 100); // H3 uses around 100 ms per frame
glass = std::make_shared<CShowableAnim>(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 1000); // H3 scales this nicely for AI turn duration, don't have anything like that in vcmi
}
CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
@ -880,7 +880,7 @@ void CInfoBar::showDate()
playNewDaySound();
state = DATE;
visibleInfo = std::make_shared<VisibleDateInfo>();
setTimer(3000);
setTimer(3000); // confirmed to match H3
redraw();
}

View File

@ -264,13 +264,13 @@ void CAnimImage::playerColored(PlayerColor currPlayer)
anim->getImage(0, group)->playerColored(player);
}
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group, uint8_t alpha):
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
anim(std::make_shared<CAnimation>(name)),
group(Group),
frame(0),
first(0),
frameDelay(Delay),
value(0),
frameTimeTotal(frameTime),
frameTimePassed(0),
flags(Flags),
xOffset(0),
yOffset(0),
@ -311,7 +311,7 @@ bool CShowableAnim::set(size_t Group, size_t from, size_t to)
group = Group;
frame = first = from;
last = max;
value = 0;
frameTimePassed = 0;
return true;
}
@ -328,13 +328,13 @@ bool CShowableAnim::set(size_t Group)
group = Group;
last = anim->size(Group);
}
frame = value = 0;
frame = 0;
frameTimePassed = 0;
return true;
}
void CShowableAnim::reset()
{
value = 0;
frame = first;
if (callback)
@ -358,9 +358,11 @@ void CShowableAnim::show(SDL_Surface * to)
if ((flags & PLAY_ONCE) && frame + 1 == last)
return;
if ( ++value == frameDelay )
frameTimePassed += GH.mainFPSmng->getElapsedMilliseconds();
if(frameTimePassed >= frameTimeTotal)
{
value = 0;
frameTimePassed -= frameTimeTotal;
if ( ++frame >= last)
reset();
}
@ -395,7 +397,7 @@ void CShowableAnim::rotate(bool on, bool vertical)
}
CCreatureAnim::CCreatureAnim(int x, int y, std::string name, ui8 flags, ECreatureAnimType type):
CShowableAnim(x,y,name,flags,4,size_t(type))
CShowableAnim(x, y, name, flags, 100, size_t(type)) // H3 uses 100 ms per frame, irregardless of battle speed settings
{
xOffset = 0;
yOffset = 0;

View File

@ -130,9 +130,11 @@ protected:
size_t first, last; //animation range
//TODO: replace with time delay(needed for battles)
ui32 frameDelay;//delay in frames of each image
ui32 value;//how many times current frame was showed
/// total time on scren for each frame in animation
ui32 frameTimeTotal;
/// how long was current frame visible on screen
ui32 frameTimePassed;
ui8 flags;//Flags from EFlags enum
@ -151,7 +153,7 @@ public:
//Set per-surface alpha, 0 = transparent, 255 = opaque
void setAlpha(ui32 alphaValue);
CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0, uint8_t alpha = UINT8_MAX);
CShowableAnim(int x, int y, std::string name, ui8 flags, ui32 frameTime, size_t Group=0, uint8_t alpha = UINT8_MAX);
~CShowableAnim();
//set animation to group or part of group

View File

@ -47,7 +47,7 @@
#include <SDL_events.h>
CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town, const CStructure * Str)
: CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE),
: CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE, BUILDING_FRAME_TIME),
parent(Par),
town(Town),
str(Str),

View File

@ -45,9 +45,11 @@ public:
enum EBuildingCreationAnimationPhases : uint32_t
{
BUILDING_APPEAR_TIMEPOINT = 500, //500 msec building appears: 0->100% transparency
BUILDING_WHITE_BORDER_TIMEPOINT = 1000, //500 msec border glows from white to yellow
BUILDING_YELLOW_BORDER_TIMEPOINT = 1500, //500 msec border glows from yellow to normal
BUILD_ANIMATION_FINISHED_TIMEPOINT = 2500 //1000 msec delay, nothing happens
BUILDING_WHITE_BORDER_TIMEPOINT = 900, //400 msec border glows from white to yellow
BUILDING_YELLOW_BORDER_TIMEPOINT = 1100, //200 msec border glows from yellow to normal (dark orange)
BUILD_ANIMATION_FINISHED_TIMEPOINT = 2100, // 1000msec once border is back to yellow nothing happens (this stage is basically removed by HD Mod)
BUILDING_FRAME_TIME = 150 // confirmed H3 timing: 150 ms for each building animation frame
};
/// returns building associated with this structure

View File

@ -85,28 +85,28 @@
"time" : 0.0
},
{
"time" : 0.2,
"time" : 0.25,
"red" : [ 0.5, 0.0, 0.5, 0.4 ],
"green" : [ 0.0, 1.0, 0.0, 0.0 ],
"blue" : [ 0.0, 0.0, 1.0, 0.0 ],
"alpha" : 1.0
},
{
"time" : 0.4,
"time" : 0.5,
"red" : [ 0.6, 0.6, 0.6, 0.0 ],
"green" : [ 0.0, 0.5, 0.0, 0.0 ],
"blue" : [ 0.0, 0.0, 0.5, 0.0 ],
"alpha" : 1.0
},
{
"time" : 0.6,
"time" : 0.75,
"red" : [ 0.5, 0.0, 0.5, 0.4 ],
"green" : [ 0.0, 1.0, 0.0, 0.0 ],
"blue" : [ 0.0, 0.0, 1.0, 0.0 ],
"alpha" : 1.0
},
{
"time" : 0.8,
"time" : 1.0,
},
],
}

View File

@ -172,7 +172,7 @@
"animationTime": {
"type":"object",
"additionalProperties" : false,
"required" : [ "attack", "flight", "walk", "idle" ],
"required" : [ "attack", "walk", "idle" ],
"description": "Length of several animations",
"properties":{
"attack": {
@ -183,10 +183,6 @@
"type":"number",
"description": "idle"
},
"flight": {
"type":"number",
"description": "flight"
},
"walk": {
"type":"number",
"description": "walk"

View File

@ -252,11 +252,11 @@
"type" : "object",
"additionalProperties" : false,
"default": {},
"required" : [ "animationSpeed", "mouseShadow", "cellBorders", "stackRange", "showQueue", "queueSize" ],
"required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "showQueue", "queueSize" ],
"properties" : {
"animationSpeed" : {
"speedFactor" : {
"type" : "number",
"default" : 0.63
"default" : 2
},
"mouseShadow" : {
"type":"boolean",

View File

@ -812,7 +812,7 @@ void CCreatureHandler::loadUnitAnimInfo(JsonNode & graphics, CLegacyConfigParser
JsonNode & animationTime = graphics["animationTime"];
animationTime["walk"].Float() = parser.readNumber();
animationTime["attack"].Float() = parser.readNumber();
animationTime["flight"].Float() = parser.readNumber();
parser.readNumber(); // unused value "Flight animation time" - H3 actually uses "Walk animation time" even for flying creatures
animationTime["idle"].Float() = 10.0;
JsonNode & missile = graphics["missile"];
@ -851,7 +851,6 @@ void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graph
cre->animation.walkAnimationTime = animationTime["walk"].Float();
cre->animation.idleAnimationTime = animationTime["idle"].Float();
cre->animation.attackAnimationTime = animationTime["attack"].Float();
cre->animation.flightAnimationDistance = animationTime["flight"].Float(); //?
const JsonNode & missile = graphics["missile"];
const JsonNode & offsets = missile["offset"];

View File

@ -74,7 +74,7 @@ public:
};
double timeBetweenFidgets, idleAnimationTime,
walkAnimationTime, attackAnimationTime, flightAnimationDistance;
walkAnimationTime, attackAnimationTime;
int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX,
upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY;
@ -91,7 +91,13 @@ public:
h & idleAnimationTime;
h & walkAnimationTime;
h & attackAnimationTime;
h & flightAnimationDistance;
if (version < 814)
{
float unused = 0.f;
h & unused;
}
h & upperRightMissleOffsetX;
h & rightMissleOffsetX;
h & lowerRightMissleOffsetX;

View File

@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
const ui32 SERIALIZATION_VERSION = 813;
const ui32 SERIALIZATION_VERSION = 814;
const ui32 MINIMAL_SERIALIZATION_VERSION = 813;
const std::string SAVEGAME_MAGIC = "VCMISVG";