mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	All battle effects now use time-based timings
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -521,7 +521,7 @@ void BattleInterface::displaySpellHit(const CSpell * spell, BattleHex destinatio | ||||
| void BattleInterface::setAnimSpeed(int set) | ||||
| { | ||||
| 	Settings speed = settings.write["battle"]["animationSpeed"]; | ||||
| 	speed->Float() = float(set) / 100; | ||||
| 	speed->Float() = float(set); | ||||
| } | ||||
|  | ||||
| int BattleInterface::getAnimSpeed() const | ||||
|   | ||||
| @@ -438,13 +438,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; | ||||
| @@ -294,13 +296,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)); | ||||
| } | ||||
| @@ -321,6 +323,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)) | ||||
| 	{ | ||||
| @@ -330,9 +333,10 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point | ||||
| 		missileProjectile->animation = getProjectileImage(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 | ||||
|   | ||||
| @@ -56,20 +56,24 @@ 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; | ||||
| 	// base speed for all H3 animations is 10/20/30 frames per second (100/50/33 ms per frame) | ||||
| 	const float baseSpeed = 10.f; | ||||
| 	const float speedMult = static_cast<float>(settings["battle"]["animationSpeed"].Float()); | ||||
| 	const float speed = baseSpeed / speedMult; | ||||
| 	const float speed = baseSpeed * speedMult; | ||||
|  | ||||
| 	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; | ||||
|  | ||||
| 	case ECreatureAnimType::SHOOT_UP: | ||||
| 	case ECreatureAnimType::SHOOT_FRONT: | ||||
| @@ -80,7 +84,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 +99,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 +120,51 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c | ||||
|  | ||||
| float AnimationControls::getProjectileSpeed() | ||||
| { | ||||
| 	// H3 speed: 1250/2500/3750 pixels per second | ||||
| 	return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 1250); | ||||
| } | ||||
|  | ||||
| float AnimationControls::getRayProjectileSpeed() | ||||
| { | ||||
| 	// H3 speed: 4000/8000/12000 pixels per second | ||||
| 	return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 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>(settings["battle"]["animationSpeed"].Float() * 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>(settings["battle"]["animationSpeed"].Float() * 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 * settings["battle"]["animationSpeed"].Float() / 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 * settings["battle"]["animationSpeed"].Float() / creature->animation.walkAnimationTime); | ||||
| } | ||||
|  | ||||
| float AnimationControls::getFadeInDuration() | ||||
| { | ||||
| 	return 1.0f / settings["battle"]["animationSpeed"].Float(); | ||||
| 	// H3 speed: 500/250/166 ms | ||||
| 	return 0.5f / settings["battle"]["animationSpeed"].Float(); | ||||
| } | ||||
|  | ||||
| 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 +425,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); | ||||
| } | ||||
|   | ||||
| @@ -31,19 +31,22 @@ namespace AnimationControls | ||||
| 	/// 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 | ||||
|   | ||||
| @@ -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, | ||||
| 			}, | ||||
| 		], | ||||
| 	} | ||||
|   | ||||
| @@ -851,7 +851,7 @@ 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(); //? | ||||
| 	cre->animation.missileVelocityFactor = animationTime["missile"].Float(); | ||||
|  | ||||
| 	const JsonNode & missile = graphics["missile"]; | ||||
| 	const JsonNode & offsets = missile["offset"]; | ||||
|   | ||||
| @@ -73,7 +73,7 @@ public: | ||||
| 		}; | ||||
|  | ||||
| 		double timeBetweenFidgets, idleAnimationTime, | ||||
| 			   walkAnimationTime, attackAnimationTime, flightAnimationDistance; | ||||
| 			   walkAnimationTime, attackAnimationTime, missileVelocityFactor; | ||||
| 		int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, | ||||
| 		    upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; | ||||
|  | ||||
| @@ -90,7 +90,7 @@ public: | ||||
| 			h & idleAnimationTime; | ||||
| 			h & walkAnimationTime; | ||||
| 			h & attackAnimationTime; | ||||
| 			h & flightAnimationDistance; | ||||
| 			h & missileVelocityFactor; | ||||
| 			h & upperRightMissleOffsetX; | ||||
| 			h & rightMissleOffsetX; | ||||
| 			h & lowerRightMissleOffsetX; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user