mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #1515 from IvanSavenko/time_based_animations
Time based animations
This commit is contained in:
		| @@ -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() | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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()); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
| 			}, | ||||
| 		], | ||||
| 	} | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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"]; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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"; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user