mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Introduced animation phases for beter ordering of visuals in battles
This commit is contained in:
		| @@ -1017,7 +1017,7 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba) | ||||
|  | ||||
| 	battleInt->effectsController->displayCustomEffects(ba->customEffects); | ||||
|  | ||||
| 	battleInt->waitForAnims(); | ||||
| 	battleInt->waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	auto actionTarget = curAction->getTarget(cb.get()); | ||||
|  | ||||
| @@ -1035,6 +1035,7 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba) | ||||
| 			{ | ||||
| 				const CStack * attacked = cb->battleGetStackByID(elem.stackAttacked); | ||||
| 				battleInt->stackAttacking(attacker, attacked->getPosition(), attacked, true); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -1042,8 +1043,6 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba) | ||||
| 	{ | ||||
| 		auto attackTarget = actionTarget.at(1).hexValue; | ||||
|  | ||||
| 		//TODO: use information from BattleAttack but not curAction | ||||
|  | ||||
| 		int shift = 0; | ||||
| 		if(ba->counter() && BattleHex::mutualPosition(attackTarget, attacker->getPosition()) < 0) | ||||
| 		{ | ||||
| @@ -1058,17 +1057,15 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba) | ||||
|  | ||||
| 		if(!ba->bsa.empty()) | ||||
| 		{ | ||||
| 			const CStack * attacked = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked); | ||||
| 			battleInt->stackAttacking(attacker, ba->counter() ? BattleHex(attackTarget + shift) : attackTarget, attacked, false); | ||||
| 			const CStack * defender = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked); | ||||
| 			battleInt->stackAttacking(attacker, BattleHex(attackTarget + shift), defender, false); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//battleInt->waitForAnims(); //FIXME: freeze | ||||
| 	//battleInt->waitForAnimationCondition(EAnimationEvents::ACTION, false); // FIXME: freeze | ||||
|  | ||||
| 	if(ba->spellLike()) | ||||
| 	{ | ||||
| 		//TODO: use information from BattleAttack but not curAction | ||||
|  | ||||
| 		auto destination = actionTarget.at(0).hexValue; | ||||
| 		//display hit animation | ||||
| 		SpellID spellID = ba->spellID; | ||||
|   | ||||
| @@ -152,36 +152,18 @@ CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, BattleInte | ||||
| 	: CBattleStackAnimation(owner, _attackedInfo.defender), | ||||
| 	  attacker(_attackedInfo.attacker), | ||||
| 	  rangedAttack(_attackedInfo.indirectAttack), | ||||
| 	  killed(_attackedInfo.killed), | ||||
| 	  timeToWait(0) | ||||
| 	  killed(_attackedInfo.killed) | ||||
| { | ||||
| 	logAnim->debug("Created defence anim for %s", _attackedInfo.defender->getName()); | ||||
| } | ||||
|  | ||||
| bool CDefenceAnimation::init() | ||||
| { | ||||
| 	if(rangedAttack && attacker != nullptr && owner.projectilesController->hasActiveProjectile(attacker)) //delay hit animation | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 	logAnim->info("CDefenceAnimation::init: stack %d", stack->ID); | ||||
|  | ||||
| 	// synchronize animation with attacker, unless defending or attacked by shooter: | ||||
| 	// wait for 1/2 of attack animation | ||||
| 	if (!rangedAttack && getMyAnimType() != ECreatureAnimType::DEFENCE) | ||||
| 	{ | ||||
| 		float frameLength = AnimationControls::getCreatureAnimationSpeed( | ||||
| 								  stack->getCreature(), stackAnimation(stack).get(), getMyAnimType()); | ||||
|  | ||||
| 		timeToWait = myAnim->framesInGroup(getMyAnimType()) * frameLength / 2; | ||||
|  | ||||
| 		//FIXME: perhaps this should be pause instead? | ||||
| 		myAnim->setType(ECreatureAnimType::HOLDING); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		timeToWait = 0; | ||||
| 		startAnimation(); | ||||
| 	} | ||||
| 	CCS->soundh->playSound(getMySound()); | ||||
| 	myAnim->setType(getMyAnimType()); | ||||
| 	myAnim->onAnimationReset += [&](){ delete this; }; | ||||
|  | ||||
| 	return true; //initialized successfuly | ||||
| } | ||||
| @@ -212,25 +194,6 @@ ECreatureAnimType::Type CDefenceAnimation::getMyAnimType() | ||||
| 		return ECreatureAnimType::HITTED; | ||||
| } | ||||
|  | ||||
| void CDefenceAnimation::startAnimation() | ||||
| { | ||||
| 	CCS->soundh->playSound(getMySound()); | ||||
| 	myAnim->setType(getMyAnimType()); | ||||
| 	myAnim->onAnimationReset += [&](){ delete this; }; | ||||
| } | ||||
|  | ||||
| void CDefenceAnimation::nextFrame() | ||||
| { | ||||
| 	if (timeToWait > 0) | ||||
| 	{ | ||||
| 		timeToWait -= float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000; | ||||
| 		if (timeToWait <= 0) | ||||
| 			startAnimation(); | ||||
| 	} | ||||
|  | ||||
| 	CBattleAnimation::nextFrame(); | ||||
| } | ||||
|  | ||||
| CDefenceAnimation::~CDefenceAnimation() | ||||
| { | ||||
| 	if(killed) | ||||
| @@ -274,19 +237,7 @@ bool CMeleeAttackAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	bool toReverse = owner.getCurrentPlayerInterface()->cb->isToReverse(attackingStackPosBeforeReturn, attackedStack->getPosition(), stackFacingRight(stack), attackedStack->doubleWide(), stackFacingRight(attackedStack)); | ||||
|  | ||||
| 	if(toReverse) | ||||
| 	{ | ||||
| 		owner.stacksController->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn)); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// opponent must face attacker ( = different directions) before he can be attacked | ||||
| 	if(attackingStack && attackedStack && | ||||
| 		stackFacingRight(attackingStack) == stackFacingRight(attackedStack)) | ||||
| 		return false; | ||||
|  | ||||
| 	logAnim->info("CMeleeAttackAnimation::init: stack %d -> stack %d", stack->ID, attackedStack->ID); | ||||
| 	//reversed | ||||
|  | ||||
| 	shooting = false; | ||||
| @@ -354,6 +305,20 @@ bool CMeleeAttackAnimation::init() | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void CMeleeAttackAnimation::nextFrame() | ||||
| { | ||||
| 	size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame(); | ||||
| 	size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(group); | ||||
|  | ||||
| 	if ( currentFrame * 2 >= totalFrames ) | ||||
| 	{ | ||||
| 		if(owner.getAnimationCondition(EAnimationEvents::HIT) == false) | ||||
| 			owner.setAnimationCondition(EAnimationEvents::HIT, true); | ||||
| 	} | ||||
|  | ||||
| 	CAttackAnimation::nextFrame(); | ||||
| } | ||||
|  | ||||
| CMeleeAttackAnimation::CMeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked) | ||||
| 	: CAttackAnimation(owner, attacker, _dest, _attacked) | ||||
| { | ||||
| @@ -386,6 +351,8 @@ bool CMovementAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("CMovementAnimation::init: stack %d moves %d -> %d", stack->ID, oldPos, currentHex); | ||||
|  | ||||
| 	//reverse unit if necessary | ||||
| 	if(owner.stacksController->shouldRotate(stack, oldPos, currentHex)) | ||||
| 	{ | ||||
| @@ -500,6 +467,8 @@ bool CMovementEndAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("CMovementEndAnimation::init: stack %d", stack->ID); | ||||
|  | ||||
| 	CCS->soundh->playSound(battle_sound(stack->getCreature(), endMoving)); | ||||
|  | ||||
| 	if(!myAnim->framesInGroup(ECreatureAnimType::MOVE_END)) | ||||
| @@ -508,6 +477,7 @@ bool CMovementEndAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	myAnim->setType(ECreatureAnimType::MOVE_END); | ||||
| 	myAnim->onAnimationReset += [&](){ delete this; }; | ||||
|  | ||||
| @@ -539,6 +509,7 @@ bool CMovementStartAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("CMovementStartAnimation::init: stack %d", stack->ID); | ||||
| 	CCS->soundh->playSound(battle_sound(stack->getCreature(), startMoving)); | ||||
|  | ||||
| 	if(!myAnim->framesInGroup(ECreatureAnimType::MOVE_START)) | ||||
| @@ -566,9 +537,10 @@ bool CReverseAnimation::init() | ||||
| 		return false; //there is no such creature | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("CReverseAnimation::init: stack %d", stack->ID); | ||||
| 	if(myAnim->framesInGroup(ECreatureAnimType::TURN_L)) | ||||
| 	{ | ||||
| 		myAnim->setType(ECreatureAnimType::TURN_L); | ||||
| 		myAnim->playOnce(ECreatureAnimType::TURN_L); | ||||
| 		myAnim->onAnimationReset += std::bind(&CReverseAnimation::setupSecondPart, this); | ||||
| 	} | ||||
| 	else | ||||
| @@ -578,12 +550,6 @@ bool CReverseAnimation::init() | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| CReverseAnimation::~CReverseAnimation() | ||||
| { | ||||
| 	if( stack && stack->alive() )//don't do that if stack is dead | ||||
| 		myAnim->setType(ECreatureAnimType::HOLDING); | ||||
| } | ||||
|  | ||||
| void CBattleStackAnimation::rotateStack(BattleHex hex) | ||||
| { | ||||
| 	setStackFacingRight(stack, !stackFacingRight(stack)); | ||||
| @@ -603,7 +569,7 @@ void CReverseAnimation::setupSecondPart() | ||||
|  | ||||
| 	if(myAnim->framesInGroup(ECreatureAnimType::TURN_R)) | ||||
| 	{ | ||||
| 		myAnim->setType(ECreatureAnimType::TURN_R); | ||||
| 		myAnim->playOnce(ECreatureAnimType::TURN_R); | ||||
| 		myAnim->onAnimationReset += [&](){ delete this; }; | ||||
| 	} | ||||
| 	else | ||||
| @@ -618,6 +584,7 @@ bool CResurrectionAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("CResurrectionAnimation::init: stack %d", stack->ID); | ||||
| 	myAnim->playOnce(ECreatureAnimType::RESURRECTION); | ||||
| 	myAnim->onAnimationReset += [&](){ delete this; }; | ||||
|  | ||||
| @@ -630,16 +597,10 @@ CResurrectionAnimation::CResurrectionAnimation(BattleInterface & owner, const CS | ||||
|  | ||||
| } | ||||
|  | ||||
| CResurrectionAnimation::~CResurrectionAnimation() | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| CRangedAttackAnimation::CRangedAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender) | ||||
| 	: CAttackAnimation(owner, attacker, dest_, defender), | ||||
| 	  projectileEmitted(false) | ||||
| { | ||||
| 	logAnim->info("Ranged attack animation created"); | ||||
| } | ||||
|  | ||||
| bool CRangedAttackAnimation::init() | ||||
| @@ -655,7 +616,8 @@ bool CRangedAttackAnimation::init() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	logAnim->info("Ranged attack animation initialized"); | ||||
| 	logAnim->info("CRangedAttackAnimation::init: stack %d", stack->ID); | ||||
|  | ||||
| 	setAnimationGroup(); | ||||
| 	initializeProjectile(); | ||||
| 	shooting = true; | ||||
| @@ -726,18 +688,17 @@ void CRangedAttackAnimation::nextFrame() | ||||
| 		if (owner.projectilesController->hasActiveProjectile(attackingStack)) | ||||
| 			stackAnimation(attackingStack)->pause(); | ||||
| 		else | ||||
| 		{ | ||||
| 			stackAnimation(attackingStack)->play(); | ||||
| 			if(owner.getAnimationCondition(EAnimationEvents::HIT) == false) | ||||
| 				owner.setAnimationCondition(EAnimationEvents::HIT, true); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	CAttackAnimation::nextFrame(); | ||||
|  | ||||
| 	if (!projectileEmitted) | ||||
| 	{ | ||||
| 		logAnim->info("Ranged attack executing, %d / %d / %d", | ||||
| 					  stackAnimation(attackingStack)->getCurrentFrame(), | ||||
| 					  getAttackClimaxFrame(), | ||||
| 					  stackAnimation(attackingStack)->framesInGroup(group)); | ||||
|  | ||||
| 		// emit projectile once animation playback reached "climax" frame | ||||
| 		if ( stackAnimation(attackingStack)->getCurrentFrame() >= getAttackClimaxFrame() ) | ||||
| 		{ | ||||
| @@ -750,7 +711,6 @@ void CRangedAttackAnimation::nextFrame() | ||||
|  | ||||
| CRangedAttackAnimation::~CRangedAttackAnimation() | ||||
| { | ||||
| 	logAnim->info("Ranged attack animation is over"); | ||||
| 	//FIXME: this assert triggers under some unclear, rare conditions. Possibly - if game window is inactive and/or in foreground/minimized? | ||||
| 	assert(!owner.projectilesController->hasActiveProjectile(attackingStack)); | ||||
| 	assert(projectileEmitted); | ||||
| @@ -830,7 +790,6 @@ void CCatapultAnimation::createProjectile(const Point & from, const Point & dest | ||||
| 	owner.projectilesController->createCatapultProjectile(attackingStack, from, dest); | ||||
| } | ||||
|  | ||||
|  | ||||
| CCastAnimation::CCastAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell) | ||||
| 	: CRangedAttackAnimation(owner, attacker, dest_, defender), | ||||
| 	  spell(spell) | ||||
| @@ -908,6 +867,7 @@ CPointEffectAnimation::CPointEffectAnimation(BattleInterface & owner, soundBase: | ||||
| 	soundFinished(false), | ||||
| 	effectFinished(false) | ||||
| { | ||||
| 	logAnim->info("CPointEffectAnimation::init: effect %s", animationName); | ||||
| } | ||||
|  | ||||
| CPointEffectAnimation::CPointEffectAnimation(BattleInterface & owner, soundBase::soundID sound, std::string animationName, std::vector<BattleHex> hex, int effects): | ||||
|   | ||||
| @@ -96,16 +96,12 @@ class CDefenceAnimation : public CBattleStackAnimation | ||||
| 	ECreatureAnimType::Type getMyAnimType(); | ||||
| 	std::string getMySound(); | ||||
|  | ||||
| 	void startAnimation(); | ||||
|  | ||||
| 	const CStack * attacker; //attacking stack | ||||
| 	bool rangedAttack; //if true, stack has been attacked by shooting | ||||
| 	bool killed; //if true, stack has been killed | ||||
|  | ||||
| 	float timeToWait; // for how long this animation should be paused | ||||
| public: | ||||
| 	bool init() override; | ||||
| 	void nextFrame() override; | ||||
|  | ||||
| 	CDefenceAnimation(StackAttackedInfo _attackedInfo, BattleInterface & owner); | ||||
| 	~CDefenceAnimation(); | ||||
| @@ -128,6 +124,7 @@ class CMeleeAttackAnimation : public CAttackAnimation | ||||
| { | ||||
| public: | ||||
| 	bool init() override; | ||||
| 	void nextFrame() override; | ||||
|  | ||||
| 	CMeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked); | ||||
| }; | ||||
| @@ -187,13 +184,11 @@ public: | ||||
| /// Class responsible for animation of stack chaning direction (left <-> right) | ||||
| class CReverseAnimation : public CStackMoveAnimation | ||||
| { | ||||
| 	void setupSecondPart(); | ||||
| public: | ||||
| 	bool init() override; | ||||
|  | ||||
| 	void setupSecondPart(); | ||||
|  | ||||
| 	CReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest); | ||||
| 	~CReverseAnimation(); | ||||
| }; | ||||
|  | ||||
| /// Resurrects stack from dead state | ||||
| @@ -202,8 +197,7 @@ class CResurrectionAnimation : public CBattleStackAnimation | ||||
| public: | ||||
| 	bool init() override; | ||||
|  | ||||
| 	CResurrectionAnimation(BattleInterface & owner, const CStack * stack); | ||||
| 	~CResurrectionAnimation(); | ||||
| 	CResurrectionAnimation(BattleInterface & owner, const CStack * _stack); | ||||
| }; | ||||
|  | ||||
| class CRangedAttackAnimation : public CAttackAnimation | ||||
|   | ||||
| @@ -9,6 +9,16 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| enum class EAnimationEvents { | ||||
| 	OPENING     = 0, // battle opening sound is playing | ||||
| 	ACTION      = 1, // there are any ongoing animations | ||||
| 	MOVEMENT    = 2, // stacks are moving or turning around | ||||
| 	ATTACK      = 3, // attack and defense animations are playing | ||||
| 	HIT         = 4, // hit & death animations are playing | ||||
| 	PROJECTILES = 5, // there are any flying projectiles | ||||
| 	COUNT | ||||
| }; | ||||
|  | ||||
| namespace EBattleEffect | ||||
| { | ||||
| 	enum EBattleEffect | ||||
| @@ -38,7 +48,7 @@ enum Type | ||||
| 	IDLE       = 1, // idling movement that happens from time to time | ||||
| 	DEFEAT     = 2, // played when army loses stack or on friendly fire | ||||
| 	VICTORY    = 3, // when enemy stack killed or huge damage is dealt | ||||
| 	CAST_SPELL = 4 // spellcasting | ||||
| 	CAST_SPELL = 4  // spellcasting | ||||
| }; | ||||
| } | ||||
|  | ||||
| @@ -46,28 +56,28 @@ namespace ECreatureAnimType | ||||
| { | ||||
| enum Type // list of creature animations, numbers were taken from def files | ||||
| { | ||||
| 	MOVING          =0, | ||||
| 	MOUSEON         =1, | ||||
| 	HOLDING         =2, | ||||
| 	HITTED          =3, | ||||
| 	DEFENCE         =4, | ||||
| 	DEATH           =5, | ||||
| 	DEATH_RANGED    =6, | ||||
| 	TURN_L          =7, | ||||
| 	TURN_R          =8, | ||||
| 	//TURN_L2       =9, //unused - identical to TURN_L | ||||
| 	//TURN_R2       =10,//unused - identical to TURN_R | ||||
| 	ATTACK_UP       =11, | ||||
| 	ATTACK_FRONT    =12, | ||||
| 	ATTACK_DOWN     =13, | ||||
| 	SHOOT_UP        =14, | ||||
| 	SHOOT_FRONT     =15, | ||||
| 	SHOOT_DOWN      =16, | ||||
| 	CAST_UP         =17, | ||||
| 	CAST_FRONT      =18, | ||||
| 	CAST_DOWN       =19, | ||||
| 	MOVE_START      =20, | ||||
| 	MOVE_END        =21, | ||||
| 	MOVING          = 0, | ||||
| 	MOUSEON         = 1, | ||||
| 	HOLDING         = 2,  // base idling animation | ||||
| 	HITTED          = 3,  // base animation for when stack is taking damage | ||||
| 	DEFENCE         = 4,  // alternative animation for defending in melee if stack spent its action on defending | ||||
| 	DEATH           = 5, | ||||
| 	DEATH_RANGED    = 6,  // alternative animation for when stack is killed by ranged attack | ||||
| 	TURN_L          = 7, | ||||
| 	TURN_R          = 8, | ||||
| 	//TURN_L2       = 9,  //unused - identical to TURN_L | ||||
| 	//TURN_R2       = 10, //unused - identical to TURN_R | ||||
| 	ATTACK_UP       = 11, | ||||
| 	ATTACK_FRONT    = 12, | ||||
| 	ATTACK_DOWN     = 13, | ||||
| 	SHOOT_UP        = 14, | ||||
| 	SHOOT_FRONT     = 15, | ||||
| 	SHOOT_DOWN      = 16, | ||||
| 	CAST_UP         = 17, | ||||
| 	CAST_FRONT      = 18, | ||||
| 	CAST_DOWN       = 19, | ||||
| 	MOVE_START      = 20,  // small animation to be played before MOVING | ||||
| 	MOVE_END        = 21,  // small animation to be played after MOVING | ||||
|  | ||||
| 	DEAD            = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here | ||||
| 	DEAD_RANGED     = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here | ||||
|   | ||||
| @@ -62,6 +62,8 @@ void BattleEffectsController::displayCustomEffects(const std::vector<CustomEffec | ||||
|  | ||||
| void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bte) | ||||
| { | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	const CStack * stack = owner.curInt->cb->battleGetStackByID(bte.stackID); | ||||
| 	if(!stack) | ||||
| 	{ | ||||
| @@ -95,11 +97,13 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt | ||||
| 		default: | ||||
| 			return; | ||||
| 	} | ||||
| 	//waitForAnims(); //fixme: freezes game :? | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleEffectsController::startAction(const BattleAction* action) | ||||
| { | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	const CStack *stack = owner.curInt->cb->battleGetStackByID(action->stackNumber); | ||||
|  | ||||
| 	switch(action->actionType) | ||||
| @@ -121,6 +125,8 @@ void BattleEffectsController::startAction(const BattleAction* action) | ||||
| 			displayEffect(EBattleEffect::REGENERATION, soundBase::REGENER, actionTarget.at(0).hexValue); | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleEffectsController::collectRenderableObjects(BattleRenderer & renderer) | ||||
|   | ||||
| @@ -42,19 +42,30 @@ | ||||
| #include "../../lib/NetPacks.h" | ||||
| #include "../../lib/UnlockGuard.h" | ||||
|  | ||||
| CondSh<bool> BattleInterface::animsAreDisplayed(false); | ||||
| CondSh<BattleAction *> BattleInterface::givenCommand(nullptr); | ||||
|  | ||||
| BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, | ||||
| 		const CGHeroInstance *hero1, const CGHeroInstance *hero2, | ||||
| 		const SDL_Rect & myRect, | ||||
| 		std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt) | ||||
| 	: attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), | ||||
| 	attackerInt(att), defenderInt(defen), curInt(att), | ||||
| 	myTurn(false), moveSoundHander(-1), bresult(nullptr), battleActionsStarted(false) | ||||
| 		std::shared_ptr<CPlayerInterface> att, | ||||
| 		std::shared_ptr<CPlayerInterface> defen, | ||||
| 		std::shared_ptr<CPlayerInterface> spectatorInt) | ||||
| 	: attackingHeroInstance(hero1) | ||||
| 	, defendingHeroInstance(hero2) | ||||
| 	, animCount(0) | ||||
| 	, attackerInt(att) | ||||
| 	, defenderInt(defen) | ||||
| 	, curInt(att) | ||||
| 	, myTurn(false) | ||||
| 	, moveSoundHander(-1) | ||||
| 	, bresult(nullptr) | ||||
| 	, battleActionsStarted(false) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION; | ||||
|  | ||||
| 	for ( auto & event : animationEvents) | ||||
| 		event.setn(false); | ||||
|  | ||||
| 	if(spectatorInt) | ||||
| 	{ | ||||
| 		curInt = spectatorInt; | ||||
| @@ -65,7 +76,6 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet * | ||||
| 		curInt = defenderInt; | ||||
| 	} | ||||
|  | ||||
| 	animsAreDisplayed.setn(false); | ||||
| 	pos = myRect; | ||||
| 	strongInterest = true; | ||||
| 	givenCommand.setn(nullptr); | ||||
| @@ -204,7 +214,9 @@ BattleInterface::~BattleInterface() | ||||
| 		const auto & terrain = *(LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType); | ||||
| 		CCS->musich->playMusicFromSet("terrain", terrain.name, true, false); | ||||
| 	} | ||||
| 	animsAreDisplayed.setn(false); | ||||
| 	assert(getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	setAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleInterface::setPrintCellBorders(bool set) | ||||
| @@ -367,7 +379,7 @@ void BattleInterface::stackAttacking( const CStack *attacker, BattleHex dest, co | ||||
|  | ||||
| void BattleInterface::newRoundFirst( int round ) | ||||
| { | ||||
| 	waitForAnims(); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleInterface::newRound(int number) | ||||
| @@ -455,10 +467,7 @@ void BattleInterface::gateStateChanged(const EGateState state) | ||||
| void BattleInterface::battleFinished(const BattleResult& br) | ||||
| { | ||||
| 	bresult = &br; | ||||
| 	{ | ||||
| 		auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim); | ||||
| 		animsAreDisplayed.waitUntil(false); | ||||
| 	} | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	stacksController->setActiveStack(nullptr); | ||||
| 	displayBattleFinished(); | ||||
| } | ||||
| @@ -519,7 +528,7 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	waitForAnims(); //wait for projectile animation | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false);//wait for projectile animation | ||||
|  | ||||
| 	displaySpellHit(spellID, sc->tile); | ||||
|  | ||||
| @@ -539,7 +548,7 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) | ||||
| 			effectsController->displayEffect(EBattleEffect::EBattleEffect(elem.effect), stack->getPosition()); | ||||
| 	} | ||||
|  | ||||
| 	waitForAnims(); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	//mana absorption | ||||
| 	if (sc->manaGained > 0) | ||||
| 	{ | ||||
| @@ -548,7 +557,7 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) | ||||
| 		stacksController->addNewAnim(new CPointEffectAnimation(*this, soundBase::invalid, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero)); | ||||
| 		stacksController->addNewAnim(new CPointEffectAnimation(*this, soundBase::invalid, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero)); | ||||
| 	} | ||||
| 	waitForAnims(); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) | ||||
| @@ -776,12 +785,6 @@ void BattleInterface::startAction(const BattleAction* action) | ||||
| 	effectsController->startAction(action); | ||||
| } | ||||
|  | ||||
| void BattleInterface::waitForAnims() | ||||
| { | ||||
| 	auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim); | ||||
| 	animsAreDisplayed.waitWhileTrue(); | ||||
| } | ||||
|  | ||||
| void BattleInterface::tacticPhaseEnd() | ||||
| { | ||||
| 	stacksController->setActiveStack(nullptr); | ||||
| @@ -800,7 +803,7 @@ void BattleInterface::tacticNextStack(const CStack * current) | ||||
| 		current = stacksController->getActiveStack(); | ||||
|  | ||||
| 	//no switching stacks when the current one is moving | ||||
| 	waitForAnims(); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE); | ||||
| 	vstd::erase_if (stacksOfMine, &immobile); | ||||
| @@ -959,3 +962,40 @@ void BattleInterface::castThisSpell(SpellID spellID) | ||||
| { | ||||
| 	actionsController->castThisSpell(spellID); | ||||
| } | ||||
|  | ||||
| void BattleInterface::setAnimationCondition( EAnimationEvents event, bool state) | ||||
| { | ||||
| 	logAnim->info("setAnimationCondition: %d -> %s", static_cast<int>(event), state ? "ON" : "OFF"); | ||||
|  | ||||
| 	size_t index = static_cast<size_t>(event); | ||||
| 	animationEvents[index].setn(state); | ||||
|  | ||||
| 	for (auto it = awaitingEvents.begin(); it != awaitingEvents.end();) | ||||
| 	{ | ||||
| 		if (it->event == event && it->eventState == state) | ||||
| 		{ | ||||
| 			it->action(); | ||||
| 			it = awaitingEvents.erase(it); | ||||
| 		} | ||||
| 		else | ||||
| 			++it; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool BattleInterface::getAnimationCondition( EAnimationEvents event) | ||||
| { | ||||
| 	size_t index = static_cast<size_t>(event); | ||||
| 	return animationEvents[index].get(); | ||||
| } | ||||
|  | ||||
| void BattleInterface::waitForAnimationCondition( EAnimationEvents event, bool state) | ||||
| { | ||||
| 	auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim); | ||||
| 	size_t index = static_cast<size_t>(event); | ||||
| 	animationEvents[index].waitUntil(state); | ||||
| } | ||||
|  | ||||
| void BattleInterface::executeOnAnimationCondition( EAnimationEvents event, bool state, const AwaitingAnimationAction & action) | ||||
| { | ||||
| 	awaitingEvents.push_back({action, event, state}); | ||||
| } | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include "BattleConstants.h" | ||||
| #include "../gui/CIntObject.h" | ||||
| #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation | ||||
| #include "../../lib/CondSh.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -21,7 +22,6 @@ class CStack; | ||||
| struct BattleResult; | ||||
| struct BattleSpellCast; | ||||
| struct CObstacleInstance; | ||||
| template <typename T> struct CondSh; | ||||
| struct SetStackEffect; | ||||
| class BattleAction; | ||||
| class CGTownInstance; | ||||
| @@ -95,6 +95,20 @@ private: | ||||
| 	bool battleActionsStarted; //used for delaying battle actions until intro sound stops | ||||
| 	int battleIntroSoundChannel; //required as variable for disabling it via ESC key | ||||
|  | ||||
| 	using AwaitingAnimationAction = std::function<void()>; | ||||
|  | ||||
| 	struct AwaitingAnimationEvents { | ||||
| 		AwaitingAnimationAction action; | ||||
| 		EAnimationEvents event; | ||||
| 		bool eventState; | ||||
| 	}; | ||||
|  | ||||
| 	/// Conditional variables that are set depending on ongoing animations on the battlefield | ||||
| 	std::array< CondSh<bool>, static_cast<size_t>(EAnimationEvents::COUNT)> animationEvents; | ||||
|  | ||||
| 	/// List of events that are waiting to be triggered | ||||
| 	std::vector<AwaitingAnimationEvents> awaitingEvents; | ||||
|  | ||||
| 	void trySetActivePlayer( PlayerColor player ); // if in hotseat, will activate interface of chosen player | ||||
| 	void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all | ||||
| 	void requestAutofightingAIToTakeAction(); | ||||
| @@ -116,7 +130,6 @@ public: | ||||
| 	std::unique_ptr<BattleActionsController> actionsController; | ||||
| 	std::unique_ptr<BattleEffectsController> effectsController; | ||||
|  | ||||
| 	static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims | ||||
| 	static CondSh<BattleAction *> givenCommand; //data != nullptr if we have i.e. moved current unit | ||||
|  | ||||
| 	bool myTurn; //if true, interface is active (commands can be ordered) | ||||
| @@ -136,7 +149,18 @@ public: | ||||
|  | ||||
| 	void tacticNextStack(const CStack *current); | ||||
| 	void tacticPhaseEnd(); | ||||
| 	void waitForAnims(); | ||||
|  | ||||
| 	/// sets condition to targeted state and executes any awaiting actions | ||||
| 	void setAnimationCondition( EAnimationEvents event, bool state); | ||||
|  | ||||
| 	/// returns current state of condition | ||||
| 	bool getAnimationCondition( EAnimationEvents event); | ||||
|  | ||||
| 	/// locks execution until selected condition reached targeted state | ||||
| 	void waitForAnimationCondition( EAnimationEvents event, bool state); | ||||
|  | ||||
| 	/// adds action that will be executed one selected condition reached targeted state | ||||
| 	void executeOnAnimationCondition( EAnimationEvents event, bool state, const AwaitingAnimationAction & action); | ||||
|  | ||||
| 	//napisz tu klase odpowiadajaca za wyswietlanie bitwy i obsluge uzytkownika, polecenia ma przekazywac callbackiem | ||||
| 	void activate() override; | ||||
|   | ||||
| @@ -107,7 +107,7 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr< | ||||
| 		owner.stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::invalid, spellObstacle->appearAnimation, whereTo, oi->pos, CPointEffectAnimation::WAIT_FOR_SOUND)); | ||||
|  | ||||
| 		//so when multiple obstacles are added, they show up one after another | ||||
| 		owner.waitForAnims(); | ||||
| 		owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 		obstaclesBeingPlaced.erase(obstaclesBeingPlaced.begin()); | ||||
| 		loadObstacleImage(*spellObstacle); | ||||
|   | ||||
| @@ -48,7 +48,6 @@ static double calculateCatapultParabolaY(const Point & from, const Point & dest, | ||||
|  | ||||
| void ProjectileMissile::show(Canvas & canvas) | ||||
| { | ||||
| 	logAnim->info("Projectile rendering, %d / %d", step, steps); | ||||
| 	size_t group = reverse ? 1 : 0; | ||||
| 	auto image = animation->getImage(frameNum, group, true); | ||||
|  | ||||
|   | ||||
| @@ -347,7 +347,8 @@ void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca) | ||||
| 		owner.stacksController->addNewAnim(new CPointEffectAnimation(owner, soundBase::WALLHIT, "SGEXPL.DEF", positions)); | ||||
| 	} | ||||
|  | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	owner.setAnimationCondition(EAnimationEvents::HIT, false); | ||||
|  | ||||
| 	for (auto attackInfo : ca.attackedParts) | ||||
| 	{ | ||||
|   | ||||
| @@ -148,8 +148,8 @@ void BattleStacksController::collectRenderableObjects(BattleRenderer & renderer) | ||||
| void BattleStacksController::stackReset(const CStack * stack) | ||||
| { | ||||
| 	//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end | ||||
| 	assert(!owner.animsAreDisplayed.get()); | ||||
| 	owner.waitForAnims(); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	auto iter = stackAnimation.find(stack->ID); | ||||
|  | ||||
| @@ -173,7 +173,7 @@ void BattleStacksController::stackReset(const CStack * stack) | ||||
| 		animation->shiftColor(&shifterClone); | ||||
| 	} | ||||
|  | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackAdded(const CStack * stack) | ||||
| @@ -355,6 +355,7 @@ void BattleStacksController::updateBattleAnimations() | ||||
| 			elem->tryInitialize(); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	bool hadAnimations = !currentAnimations.empty(); | ||||
| 	vstd::erase(currentAnimations, nullptr); | ||||
|  | ||||
| @@ -362,20 +363,20 @@ void BattleStacksController::updateBattleAnimations() | ||||
| 	{ | ||||
| 		//anims ended | ||||
| 		owner.controlPanel->blockUI(activeStack == nullptr); | ||||
| 		owner.animsAreDisplayed.setn(false); | ||||
| 		owner.setAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BattleStacksController::addNewAnim(CBattleAnimation *anim) | ||||
| { | ||||
| 	currentAnimations.push_back(anim); | ||||
| 	owner.animsAreDisplayed.setn(true); | ||||
| 	owner.setAnimationCondition(EAnimationEvents::ACTION, true); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackActivated(const CStack *stack) //TODO: check it all before game state is changed due to abilities | ||||
| { | ||||
| 	stackToActivate = stack; | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	if (stackToActivate) //during waiting stack may have gotten activated through show | ||||
| 		owner.activateStack(); | ||||
| } | ||||
| @@ -412,17 +413,34 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at | ||||
| 			addNewAnim(new CReverseAnimation(owner, attackedInfo.defender, attackedInfo.defender->getPosition())); | ||||
| 	} | ||||
|  | ||||
| 	// raise flag that movement phase started, starting any queued movements | ||||
| 	owner.setAnimationCondition(EAnimationEvents::MOVEMENT, true); | ||||
|  | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	owner.setAnimationCondition(EAnimationEvents::MOVEMENT, false); | ||||
|  | ||||
| 	for(auto & attackedInfo : attackedInfos) | ||||
| 	{ | ||||
| 		addNewAnim(new CDefenceAnimation(attackedInfo, owner)); | ||||
| 		bool useDefenceAnim = attackedInfo.defender->defendingAnim && !attackedInfo.indirectAttack && !attackedInfo.killed; | ||||
|  | ||||
| 		if (attackedInfo.battleEffect != EBattleEffect::INVALID) | ||||
| 			owner.effectsController->displayEffect(EBattleEffect::EBattleEffect(attackedInfo.battleEffect), attackedInfo.defender->getPosition()); | ||||
| 		EAnimationEvents usedEvent = useDefenceAnim ? EAnimationEvents::ATTACK : EAnimationEvents::HIT; | ||||
|  | ||||
| 		if (attackedInfo.spellEffect != SpellID::NONE) | ||||
| 			owner.displaySpellEffect(attackedInfo.spellEffect, attackedInfo.defender->getPosition()); | ||||
| 		owner.executeOnAnimationCondition(usedEvent, true, [=]() | ||||
| 		{ | ||||
| 			addNewAnim(new CDefenceAnimation(attackedInfo, owner)); | ||||
|  | ||||
| 			if (attackedInfo.battleEffect != EBattleEffect::INVALID) | ||||
| 				owner.effectsController->displayEffect(EBattleEffect::EBattleEffect(attackedInfo.battleEffect), attackedInfo.defender->getPosition()); | ||||
|  | ||||
| 			if (attackedInfo.spellEffect != SpellID::NONE) | ||||
| 				owner.displaySpellEffect(attackedInfo.spellEffect, attackedInfo.defender->getPosition()); | ||||
| 		}); | ||||
| 	} | ||||
| 	owner.waitForAnims(); | ||||
|  | ||||
| 	owner.setAnimationCondition(EAnimationEvents::ATTACK, true); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	owner.setAnimationCondition(EAnimationEvents::ATTACK, false); | ||||
| 	owner.setAnimationCondition(EAnimationEvents::HIT, false); | ||||
|  | ||||
| 	for (auto & attackedInfo : attackedInfos) | ||||
| 	{ | ||||
| @@ -435,7 +453,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at | ||||
| 		if (attackedInfo.cloneKilled) | ||||
| 			stackRemoved(attackedInfo.defender->ID); | ||||
| 	} | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance) | ||||
| @@ -443,23 +461,27 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH | ||||
| 	assert(destHex.size() > 0); | ||||
|  | ||||
| 	//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end | ||||
| 	assert(!owner.animsAreDisplayed.get()); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	if(shouldRotate(stack, stack->getPosition(), destHex[0])) | ||||
| 		addNewAnim(new CReverseAnimation(owner, stack, destHex[0])); | ||||
|  | ||||
| 	addNewAnim(new CMovementStartAnimation(owner, stack)); | ||||
| 	owner.waitForAnims(); | ||||
|  | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	addNewAnim(new CMovementAnimation(owner, stack, destHex, distance)); | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	addNewAnim(new CMovementEndAnimation(owner, stack, destHex.back())); | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackAttacking( const CStack *attacker, BattleHex dest, const CStack *defender, bool shooting ) | ||||
| { | ||||
| 	//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	bool needsReverse = | ||||
| 			owner.curInt->cb->isToReverse( | ||||
| 				attacker->getPosition(), | ||||
| @@ -469,21 +491,26 @@ void BattleStacksController::stackAttacking( const CStack *attacker, BattleHex d | ||||
| 				facingRight(defender)); | ||||
|  | ||||
| 	if (needsReverse) | ||||
| 		addNewAnim(new CReverseAnimation(owner, attacker, attacker->getPosition())); | ||||
|  | ||||
| 	owner.waitForAnims(); | ||||
|  | ||||
| 	if (shooting) | ||||
| 	{ | ||||
| 		addNewAnim(new CShootingAnimation(owner, attacker, dest, defender)); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		addNewAnim(new CMeleeAttackAnimation(owner, attacker, dest, defender)); | ||||
| 		owner.executeOnAnimationCondition(EAnimationEvents::MOVEMENT, true, [=]() | ||||
| 		{ | ||||
| 			addNewAnim(new CReverseAnimation(owner, attacker, attacker->getPosition())); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// do not wait - waiting will be done at stacksAreAttacked | ||||
| 	// waitForAnims(); | ||||
| 	owner.executeOnAnimationCondition(EAnimationEvents::ATTACK, true, [=]() | ||||
| 	{ | ||||
| 		if (shooting) | ||||
| 		{ | ||||
| 			addNewAnim(new CShootingAnimation(owner, attacker, dest, defender)); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			addNewAnim(new CMeleeAttackAnimation(owner, attacker, dest, defender)); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	//waiting will be done in stacksAreAttacked | ||||
| } | ||||
|  | ||||
| bool BattleStacksController::shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const | ||||
| @@ -503,8 +530,8 @@ bool BattleStacksController::shouldRotate(const CStack * stack, const BattleHex | ||||
| void BattleStacksController::endAction(const BattleAction* action) | ||||
| { | ||||
| 	//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end | ||||
| 	assert(!owner.animsAreDisplayed.get()); | ||||
| 	owner.waitForAnims(); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	//check if we should reverse stacks | ||||
| 	TStacks stacks = owner.curInt->cb->battleGetStacks(CBattleCallback::MINE_AND_ENEMY); | ||||
| @@ -518,7 +545,15 @@ void BattleStacksController::endAction(const BattleAction* action) | ||||
| 			addNewAnim(new CReverseAnimation(owner, s, s->getPosition())); | ||||
| 		} | ||||
| 	} | ||||
| 	owner.waitForAnims(); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::OPENING) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::MOVEMENT) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ATTACK) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::HIT) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::PROJECTILES) == false); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::startAction(const BattleAction* action) | ||||
|   | ||||
| @@ -18,6 +18,8 @@ template <typename T> struct CondSh | ||||
| 	boost::condition_variable cond; | ||||
| 	boost::mutex mx; | ||||
|  | ||||
| 	CondSh() : data(T()) {} | ||||
|  | ||||
| 	CondSh(T t) : data(t) {} | ||||
|  | ||||
| 	// set data | ||||
|   | ||||
		Reference in New Issue
	
	Block a user