mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implemented Bloodlust & Petrification effect
- ColorFilter is now in separate file - Moved lerp function into global.h - Bloodlust visuals mostly matches H3 - Petrify visual matches H3 - TODO: Adjust timing of all ColorFilter efects to match H3 - TODO: Petrify should pause stack animations - TODO: ColorFilter-powered effects should be configurable in Spell system
This commit is contained in:
		
							
								
								
									
										7
									
								
								Global.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Global.h
									
									
									
									
									
								
							| @@ -760,6 +760,13 @@ namespace vstd | ||||
| 		return v; | ||||
| 	} | ||||
|  | ||||
| 	//c++20 feature | ||||
| 	template<typename Arithmetic, typename Floating> | ||||
| 	Arithmetic lerp(const Arithmetic & a, const Arithmetic & b, const Floating & f) | ||||
| 	{ | ||||
| 		return a + (b - a) * f; | ||||
| 	} | ||||
|  | ||||
| 	using boost::math::round; | ||||
| } | ||||
| using vstd::operator-=; | ||||
|   | ||||
| @@ -21,6 +21,7 @@ set(client_SRCS | ||||
| 		gui/CCursorHandler.cpp | ||||
| 		gui/CGuiHandler.cpp | ||||
| 		gui/CIntObject.cpp | ||||
| 		gui/ColorFilter.cpp | ||||
| 		gui/Fonts.cpp | ||||
| 		gui/Geometries.cpp | ||||
| 		gui/SDL_Extensions.cpp | ||||
| @@ -104,6 +105,7 @@ set(client_HEADERS | ||||
| 		gui/Canvas.h | ||||
| 		gui/CCursorHandler.h | ||||
| 		gui/CGuiHandler.h | ||||
| 		gui/ColorFilter.h | ||||
| 		gui/CIntObject.h | ||||
| 		gui/Fonts.h | ||||
| 		gui/Geometries.h | ||||
|   | ||||
| @@ -18,7 +18,7 @@ CreatureCostBox::CreatureCostBox(Rect position, std::string titleText) | ||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | ||||
|  | ||||
| 	type |= REDRAW_PARENT; | ||||
| 	pos = position + pos; | ||||
| 	pos = position + pos.topLeft(); | ||||
|  | ||||
| 	title = std::make_shared<CLabel>(pos.w/2, 10, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, titleText); | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "../CPlayerInterface.h" | ||||
| #include "../gui/CCursorHandler.h" | ||||
| #include "../gui/CGuiHandler.h" | ||||
| #include "../gui/SDL_Extensions.h" | ||||
|  | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/CStack.h" | ||||
| @@ -409,7 +410,7 @@ void MovementAnimation::nextFrame() | ||||
| 	{ | ||||
| 		// Sets the position of the creature animation sprites | ||||
| 		Point coords = owner.stacksController->getStackPositionAtHex(currentHex, stack); | ||||
| 		myAnim->pos = coords; | ||||
| 		myAnim->pos.moveTo(coords); | ||||
|  | ||||
| 		// true if creature haven't reached the final destination hex | ||||
| 		if ((curentMoveIndex + 1) < destTiles.size()) | ||||
| @@ -431,7 +432,7 @@ MovementAnimation::~MovementAnimation() | ||||
| { | ||||
| 	assert(stack); | ||||
|  | ||||
| 	myAnim->pos = owner.stacksController->getStackPositionAtHex(currentHex, stack); | ||||
| 	myAnim->pos.moveTo(owner.stacksController->getStackPositionAtHex(currentHex, stack)); | ||||
|  | ||||
| 	if(owner.moveSoundHander != -1) | ||||
| 	{ | ||||
| @@ -560,7 +561,7 @@ void BattleStackAnimation::rotateStack(BattleHex hex) | ||||
| { | ||||
| 	setStackFacingRight(stack, !stackFacingRight(stack)); | ||||
|  | ||||
| 	stackAnimation(stack)->pos = owner.stacksController->getStackPositionAtHex(hex, stack); | ||||
| 	stackAnimation(stack)->pos.moveTo(owner.stacksController->getStackPositionAtHex(hex, stack)); | ||||
| } | ||||
|  | ||||
| void ReverseAnimation::setupSecondPart() | ||||
| @@ -607,41 +608,109 @@ ResurrectionAnimation::ResurrectionAnimation(BattleInterface & owner, const CSta | ||||
|  | ||||
| } | ||||
|  | ||||
| bool FadingAnimation::init() | ||||
| bool ColorTransformAnimation::init() | ||||
| { | ||||
| 	logAnim->info("FadingAnimation::init: stack %s", stack->getName()); | ||||
| 	//TODO: pause animation? | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void FadingAnimation::nextFrame() | ||||
| void ColorTransformAnimation::nextFrame() | ||||
| { | ||||
| 	float elapsed  = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; | ||||
| 	float fullTime = AnimationControls::getFadeInDuration(); | ||||
| 	float delta    = elapsed / fullTime; | ||||
| 	progress += delta; | ||||
| 	totalProgress += delta; | ||||
|  | ||||
| 	if (progress > 1.0f) | ||||
| 		progress = 1.0f; | ||||
| 	size_t index = 0; | ||||
|  | ||||
| 	uint8_t factor = stack->cloned ? 128 : 255; | ||||
| 	uint8_t blue   = stack->cloned ? 128 : 0; | ||||
| 	uint8_t alpha  = CSDL_Ext::lerp(from, dest, progress); | ||||
| 	while (index < timePoints.size() && timePoints[index] < totalProgress ) | ||||
| 		++index; | ||||
|  | ||||
| 	ColorShifterRange shifterFade ({0, 0, blue, 0}, {factor, factor, 255, alpha}); | ||||
| 	stackAnimation(stack)->shiftColor(&shifterFade); | ||||
|  | ||||
| 	if (progress == 1.0f) | ||||
| 	if (index == timePoints.size()) | ||||
| 	{ | ||||
| 		//end of animation. Apply ColorShifter using final values and die | ||||
| 		const auto & shifter = steps[index - 1]; | ||||
| 		owner.stacksController->setStackColorFilter(shifter, stack, spell, false); | ||||
| 		delete this; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	assert(index != 0); | ||||
|  | ||||
| 	const auto & prevShifter = steps[index - 1]; | ||||
| 	const auto & nextShifter = steps[index]; | ||||
|  | ||||
| 	float prevPoint = timePoints[index-1]; | ||||
| 	float nextPoint = timePoints[index]; | ||||
| 	float localProgress = totalProgress - prevPoint; | ||||
| 	float stepDuration = (nextPoint - prevPoint); | ||||
| 	float factor = localProgress / stepDuration; | ||||
|  | ||||
| 	auto shifter = ColorFilter::genInterpolated(prevShifter, nextShifter, factor); | ||||
|  | ||||
| 	owner.stacksController->setStackColorFilter(shifter, stack, spell, true); | ||||
| } | ||||
|  | ||||
| FadingAnimation::FadingAnimation(BattleInterface & owner, const CStack * _stack, uint8_t alphaFrom, uint8_t alphaDest): | ||||
| ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell): | ||||
| 	BattleStackAnimation(owner, _stack), | ||||
| 	from(alphaFrom), | ||||
| 	dest(alphaDest) | ||||
| 	spell(spell), | ||||
| 	totalProgress(0.f) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation * ColorTransformAnimation::bloodlustAnimation(BattleInterface & owner, const CStack * stack, const CSpell * spell) | ||||
| { | ||||
| 	auto result = new ColorTransformAnimation(owner, stack, spell); | ||||
|  | ||||
| 	result->steps.push_back(ColorFilter::genEmptyShifter()); | ||||
| 	result->steps.push_back(ColorFilter::genMuxerShifter( { 0.3, 0.0, 0.3, 0.4}, { 0, 1, 0, 0}, { 0, 0, 1, 0}, 1.f )); | ||||
| 	result->steps.push_back(ColorFilter::genMuxerShifter( { 0.3, 0.3, 0.3, 0.1}, { 0, 0.5, 0, 0}, { 0, 0.5, 0, 0}, 1.f )); | ||||
| 	result->steps.push_back(ColorFilter::genMuxerShifter( { 0.3, 0.0, 0.3, 0.4}, { 0, 1, 0, 0}, { 0, 0, 1, 0}, 1.f )); | ||||
| 	result->steps.push_back(ColorFilter::genEmptyShifter()); | ||||
|  | ||||
| 	result->timePoints.push_back(0.0f); | ||||
| 	result->timePoints.push_back(0.2f); | ||||
| 	result->timePoints.push_back(0.4f); | ||||
| 	result->timePoints.push_back(0.6f); | ||||
| 	result->timePoints.push_back(0.8f); | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation * ColorTransformAnimation::cloneAnimation(BattleInterface & owner, const CStack * stack, const CSpell * spell) | ||||
| { | ||||
| 	auto result = new ColorTransformAnimation(owner, stack, spell); | ||||
| 	result->steps.push_back(ColorFilter::genRangeShifter( 0, 0, 0.5, 0.5, 0.5, 1.0)); | ||||
| 	result->timePoints.push_back(0.f); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation * ColorTransformAnimation::petrifyAnimation(BattleInterface & owner, const CStack * stack, const CSpell * spell) | ||||
| { | ||||
| 	auto result = new ColorTransformAnimation(owner, stack, spell); | ||||
| 	result->steps.push_back(ColorFilter::genEmptyShifter()); | ||||
| 	result->steps.push_back(ColorFilter::genGrayscaleShifter()); | ||||
| 	result->timePoints.push_back(0.f); | ||||
| 	result->timePoints.push_back(1.f); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation * ColorTransformAnimation::fadeInAnimation(BattleInterface & owner, const CStack * stack) | ||||
| { | ||||
| 	auto result = new ColorTransformAnimation(owner, stack, nullptr); | ||||
| 	result->steps.push_back(ColorFilter::genAlphaShifter(0.f)); | ||||
| 	result->steps.push_back(ColorFilter::genEmptyShifter()); | ||||
| 	result->timePoints.push_back(0.f); | ||||
| 	result->timePoints.push_back(1.f); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation * ColorTransformAnimation::fadeOutAnimation(BattleInterface & owner, const CStack * stack) | ||||
| { | ||||
| 	auto result = fadeInAnimation(owner, stack); | ||||
| 	std::swap(result->steps[0], result->steps[1]); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| RangedAttackAnimation::RangedAttackAnimation(BattleInterface & owner_, const CStack * attacker, BattleHex dest_, const CStack * defender) | ||||
| 	: AttackAnimation(owner_, attacker, dest_, defender), | ||||
| @@ -735,11 +804,8 @@ void RangedAttackAnimation::nextFrame() | ||||
| 	// animation should be paused if there is an active projectile | ||||
| 	if (projectileEmitted) | ||||
| 	{ | ||||
| 		if (owner.projectilesController->hasActiveProjectile(attackingStack)) | ||||
| 			stackAnimation(attackingStack)->pause(); | ||||
| 		else | ||||
| 		if (!owner.projectilesController->hasActiveProjectile(attackingStack, false)) | ||||
| 		{ | ||||
| 			stackAnimation(attackingStack)->play(); | ||||
| 			if(owner.getAnimationCondition(EAnimationEvents::HIT) == false) | ||||
| 				owner.setAnimationCondition(EAnimationEvents::HIT, true); | ||||
| 		} | ||||
| @@ -753,7 +819,6 @@ void RangedAttackAnimation::nextFrame() | ||||
| 		if ( stackAnimation(attackingStack)->getCurrentFrame() >= getAttackClimaxFrame() ) | ||||
| 		{ | ||||
| 			emitProjectile(); | ||||
| 			stackAnimation(attackingStack)->pause(); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| @@ -761,7 +826,7 @@ void RangedAttackAnimation::nextFrame() | ||||
|  | ||||
| RangedAttackAnimation::~RangedAttackAnimation() | ||||
| { | ||||
| 	assert(!owner.projectilesController->hasActiveProjectile(attackingStack)); | ||||
| 	assert(!owner.projectilesController->hasActiveProjectile(attackingStack, false)); | ||||
| 	assert(projectileEmitted); | ||||
|  | ||||
| 	// FIXME: is this possible? Animation is over but we're yet to fire projectile? | ||||
| @@ -822,7 +887,7 @@ void CatapultAnimation::nextFrame() | ||||
| 	if ( !projectileEmitted) | ||||
| 		return; | ||||
|  | ||||
| 	if (owner.projectilesController->hasActiveProjectile(attackingStack)) | ||||
| 	if (owner.projectilesController->hasActiveProjectile(attackingStack, false)) | ||||
| 		return; | ||||
|  | ||||
| 	explosionEmitted = true; | ||||
| @@ -1171,7 +1236,7 @@ void HeroCastAnimation::nextFrame() | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!owner.projectilesController->hasActiveProjectile(nullptr)) | ||||
| 	if (!owner.projectilesController->hasActiveProjectile(nullptr, false)) | ||||
| 	{ | ||||
| 		emitAnimationEvent(); | ||||
| 		//TODO: check H3 - it is possible that hero animation should be paused until hit effect is over, not just projectile | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../lib/battle/BattleHex.h" | ||||
| #include "../gui/Geometries.h" | ||||
| #include "BattleConstants.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| @@ -20,6 +21,8 @@ class CSpell; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| struct SDL_Color; | ||||
| class ColorFilter; | ||||
| class BattleHero; | ||||
| class CAnimation; | ||||
| class BattleInterface; | ||||
| @@ -213,17 +216,25 @@ public: | ||||
| 	ResurrectionAnimation(BattleInterface & owner, const CStack * _stack); | ||||
| }; | ||||
|  | ||||
| /// Performs fade-in or fade-out animation on stack | ||||
| class FadingAnimation : public BattleStackAnimation | ||||
| class ColorTransformAnimation : public BattleStackAnimation | ||||
| { | ||||
| 	float progress; | ||||
| 	uint8_t from; | ||||
| 	uint8_t dest; | ||||
| public: | ||||
| 	std::vector<ColorFilter> steps; | ||||
| 	std::vector<float> timePoints; | ||||
| 	const CSpell * spell; | ||||
|  | ||||
| 	float totalProgress; | ||||
|  | ||||
| 	bool init() override; | ||||
| 	void nextFrame() override; | ||||
|  | ||||
| 	FadingAnimation(BattleInterface & owner, const CStack * _stack, uint8_t alphaFrom, uint8_t alphaDest); | ||||
| 	ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell); | ||||
| public: | ||||
|  | ||||
| 	static ColorTransformAnimation * petrifyAnimation  (BattleInterface & owner, const CStack * _stack, const CSpell * spell); | ||||
| 	static ColorTransformAnimation * cloneAnimation    (BattleInterface & owner, const CStack * _stack, const CSpell * spell); | ||||
| 	static ColorTransformAnimation * bloodlustAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell); | ||||
| 	static ColorTransformAnimation * fadeInAnimation   (BattleInterface & owner, const CStack * _stack); | ||||
| 	static ColorTransformAnimation * fadeOutAnimation  (BattleInterface & owner, const CStack * _stack); | ||||
| }; | ||||
|  | ||||
| class RangedAttackAnimation : public AttackAnimation | ||||
|   | ||||
| @@ -457,6 +457,7 @@ void BattleInterface::gateStateChanged(const EGateState state) | ||||
| void BattleInterface::battleFinished(const BattleResult& br) | ||||
| { | ||||
| 	bresult = &br; | ||||
| 	assert(getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	stacksController->setActiveStack(nullptr); | ||||
| 	displayBattleFinished(); | ||||
| @@ -537,7 +538,12 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) | ||||
| 		if(stack) | ||||
| 		{ | ||||
| 			executeOnAnimationCondition(EAnimationEvents::HIT, true, [=](){ | ||||
| 				displaySpellEffect(spellID, stack->getPosition()); | ||||
| 				if (spellID == SpellID::BLOODLUST) | ||||
| 					stacksController->addNewAnim( ColorTransformAnimation::bloodlustAnimation(*this, stack, spell)); | ||||
| 				else if (spellID == SpellID::STONE_GAZE) | ||||
| 					stacksController->addNewAnim( ColorTransformAnimation::petrifyAnimation(*this, stack, spell)); | ||||
| 				else | ||||
| 					displaySpellEffect(spellID, stack->getPosition()); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| @@ -794,6 +800,7 @@ void BattleInterface::tacticNextStack(const CStack * current) | ||||
| 		current = stacksController->getActiveStack(); | ||||
|  | ||||
| 	//no switching stacks when the current one is moving | ||||
| 	assert(getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE); | ||||
|   | ||||
| @@ -56,8 +56,8 @@ void ProjectileMissile::show(Canvas & canvas) | ||||
| 		float progress = float(step) / steps; | ||||
|  | ||||
| 		Point pos { | ||||
| 			CSDL_Ext::lerp(from.x, dest.x, progress) - image->width() / 2, | ||||
| 			CSDL_Ext::lerp(from.y, dest.y, progress) - image->height() / 2, | ||||
| 			vstd::lerp(from.x, dest.x, progress) - image->width() / 2, | ||||
| 			vstd::lerp(from.y, dest.y, progress) - image->height() / 2, | ||||
| 		}; | ||||
|  | ||||
| 		canvas.draw(image, pos); | ||||
| @@ -84,7 +84,7 @@ void ProjectileCatapult::show(Canvas & canvas) | ||||
| 	{ | ||||
| 		float progress = float(step) / steps; | ||||
|  | ||||
| 		int posX = CSDL_Ext::lerp(from.x, dest.x, progress); | ||||
| 		int posX = vstd::lerp(from.x, dest.x, progress); | ||||
| 		int posY = calculateCatapultParabolaY(from, dest, posX); | ||||
| 		Point pos(posX, posY); | ||||
|  | ||||
| @@ -100,8 +100,8 @@ void ProjectileRay::show(Canvas & canvas) | ||||
| 	float progress = float(step) / steps; | ||||
|  | ||||
| 	Point curr { | ||||
| 		CSDL_Ext::lerp(from.x, dest.x, progress), | ||||
| 		CSDL_Ext::lerp(from.y, dest.y, progress), | ||||
| 		vstd::lerp(from.x, dest.x, progress), | ||||
| 		vstd::lerp(from.y, dest.y, progress), | ||||
| 	}; | ||||
|  | ||||
| 	Point length = curr - from; | ||||
| @@ -235,13 +235,13 @@ void BattleProjectileController::showProjectiles(Canvas & canvas) | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| bool BattleProjectileController::hasActiveProjectile(const CStack * stack) const | ||||
| bool BattleProjectileController::hasActiveProjectile(const CStack * stack, bool emittedOnly) const | ||||
| { | ||||
| 	int stackID = stack ? stack->ID : -1; | ||||
|  | ||||
| 	for(auto const & instance : projectiles) | ||||
| 	{ | ||||
| 		if(instance->shooterID == stackID) | ||||
| 		if(instance->shooterID == stackID && (instance->playing || !emittedOnly)) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|   | ||||
| @@ -106,7 +106,7 @@ public: | ||||
| 	void showProjectiles(Canvas & canvas); | ||||
|  | ||||
| 	/// returns true if stack has projectile that is yet to hit target | ||||
| 	bool hasActiveProjectile(const CStack * stack) const; | ||||
| 	bool hasActiveProjectile(const CStack * stack, bool emittedOnly) const; | ||||
|  | ||||
| 	/// starts rendering previously created projectile | ||||
| 	void emitStackProjectile(const CStack * stack); | ||||
|   | ||||
| @@ -327,9 +327,7 @@ bool BattleSiegeController::isAttackableByCatapult(BattleHex hex) const | ||||
|  | ||||
| void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca) | ||||
| { | ||||
| 	//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); | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
|  | ||||
| 	if (ca.attacker != -1) | ||||
| 	{ | ||||
|   | ||||
| @@ -76,15 +76,21 @@ BattleStacksController::BattleStacksController(BattleInterface & owner): | ||||
| 	amountNegative   = IImage::createFromFile("CMNUMWIN.BMP"); | ||||
| 	amountEffNeutral = IImage::createFromFile("CMNUMWIN.BMP"); | ||||
|  | ||||
| 	static const ColorShifterRangeExcept shifterNormal  ({0,0,0,0}, {150,  50, 255, 255}, {255, 231, 132, 255}); | ||||
| 	static const ColorShifterRangeExcept shifterPositive({0,0,0,0}, { 50, 255,  50, 255}, {255, 231, 132, 255}); | ||||
| 	static const ColorShifterRangeExcept shifterNegative({0,0,0,0}, {255,  50,  50, 255}, {255, 231, 132, 255}); | ||||
| 	static const ColorShifterRangeExcept shifterNeutral ({0,0,0,0}, {255, 255,  50, 255}, {255, 231, 132, 255}); | ||||
| 	static const auto shifterNormal   = ColorFilter::genRangeShifter( 0,0,0, 0.6, 0.2, 1.0 ); | ||||
| 	static const auto shifterPositive = ColorFilter::genRangeShifter( 0,0,0, 0.2, 1.0, 0.2 ); | ||||
| 	static const auto shifterNegative = ColorFilter::genRangeShifter( 0,0,0, 1.0, 0.2, 0.2 ); | ||||
| 	static const auto shifterNeutral  = ColorFilter::genRangeShifter( 0,0,0, 1.0, 1.0, 0.2 ); | ||||
|  | ||||
| 	amountNormal->adjustPalette(&shifterNormal); | ||||
| 	amountPositive->adjustPalette(&shifterPositive); | ||||
| 	amountNegative->adjustPalette(&shifterNegative); | ||||
| 	amountEffNeutral->adjustPalette(&shifterNeutral); | ||||
| 	amountNormal->adjustPalette(shifterNormal); | ||||
| 	amountPositive->adjustPalette(shifterPositive); | ||||
| 	amountNegative->adjustPalette(shifterNegative); | ||||
| 	amountEffNeutral->adjustPalette(shifterNeutral); | ||||
|  | ||||
| 	//Restore border color {255, 231, 132, 255} to its original state | ||||
| 	amountNormal->resetPalette(26); | ||||
| 	amountPositive->resetPalette(26); | ||||
| 	amountNegative->resetPalette(26); | ||||
| 	amountEffNeutral->resetPalette(26); | ||||
|  | ||||
| 	std::vector<const CStack*> stacks = owner.curInt->cb->battleGetAllStacks(true); | ||||
| 	for(const CStack * s : stacks) | ||||
| @@ -98,7 +104,7 @@ BattleHex BattleStacksController::getStackCurrentPosition(const CStack * stack) | ||||
| 	if ( !stackAnimation.at(stack->ID)->isMoving()) | ||||
| 		return stack->getPosition(); | ||||
|  | ||||
| 	if (stack->hasBonusOfType(Bonus::FLYING)) | ||||
| 	if (stack->hasBonusOfType(Bonus::FLYING) && stackAnimation.at(stack->ID)->getType() == ECreatureAnimType::MOVING ) | ||||
| 		return BattleHex::HEX_AFTER_ALL; | ||||
|  | ||||
| 	for (auto & anim : currentAnimations) | ||||
| @@ -147,9 +153,7 @@ 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.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	//owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	//reset orientation? | ||||
| 	//stackFacingRight[stack->ID] = stack->side == BattleSide::ATTACKER; | ||||
| @@ -171,13 +175,6 @@ void BattleStacksController::stackReset(const CStack * stack) | ||||
| 			addNewAnim(new ResurrectionAnimation(owner, stack)); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	//static const ColorShifterMultiplyAndAdd shifterClone ({255, 255, 0, 255}, {0, 0, 255, 0}); | ||||
| 	//if (stack->isClone()) | ||||
| 	//{ | ||||
| 	//	animation->shiftColor(&shifterClone); | ||||
| 	//} | ||||
| 	//owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackAdded(const CStack * stack, bool instant) | ||||
| @@ -213,12 +210,15 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant) | ||||
|  | ||||
| 	if (!instant) | ||||
| 	{ | ||||
| 		ColorShifterRange shifterFade ({0, 0, 0, 0}, {255, 255, 255, 0}); | ||||
| 		stackAnimation[stack->ID]->shiftColor(&shifterFade); | ||||
| 		// immediately make stack transparent, giving correct shifter time to start | ||||
| 		auto shifterFade = ColorFilter::genAlphaShifter(0); | ||||
| 		setStackColorFilter(shifterFade, stack, nullptr, true); | ||||
|  | ||||
| 		owner.executeOnAnimationCondition(EAnimationEvents::HIT, true, [=]() | ||||
| 		{ | ||||
| 			addNewAnim(new FadingAnimation(owner, stack, 0, 255)); | ||||
| 			addNewAnim(ColorTransformAnimation::fadeInAnimation(owner, stack)); | ||||
| 			if (stack->isClone()) | ||||
| 				addNewAnim(ColorTransformAnimation::cloneAnimation(owner, stack, SpellID(SpellID::CLONE).toSpell())); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -353,7 +353,23 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack * | ||||
|  | ||||
| void BattleStacksController::showStack(Canvas & canvas, const CStack * stack) | ||||
| { | ||||
| 	stackAnimation[stack->ID]->nextFrame(canvas, facingRight(stack)); // do actual blit | ||||
| 	ColorFilter fullFilter = ColorFilter::genEmptyShifter(); | ||||
| 	for (auto const & filter : stackFilterEffects) | ||||
| 	{ | ||||
| 		if (filter.target == stack) | ||||
| 			fullFilter = ColorFilter::genCombined(fullFilter, filter.effect); | ||||
| 	} | ||||
|  | ||||
| 	bool stackHasProjectile = owner.projectilesController->hasActiveProjectile(stack, true); | ||||
| 	//bool stackPetrified = stack->hasBonus(Selector::source(Bonus::SPELL_EFFECT, SpellID::STONE_GAZE)); | ||||
| 	//bool stackFrozen = stackHasProjectile || stackPetrified; | ||||
|  | ||||
| 	if (stackHasProjectile) | ||||
| 		stackAnimation[stack->ID]->pause(); | ||||
| 	else | ||||
| 		stackAnimation[stack->ID]->play(); | ||||
|  | ||||
| 	stackAnimation[stack->ID]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit | ||||
| 	stackAnimation[stack->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000); | ||||
| } | ||||
|  | ||||
| @@ -387,14 +403,6 @@ void BattleStacksController::addNewAnim(BattleAnimation *anim) | ||||
| 	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.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	if (stackToActivate) //during waiting stack may have gotten activated through show | ||||
| 		owner.activateStack(); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::stackRemoved(uint32_t stackID) | ||||
| { | ||||
| 	if (getActiveStack() && getActiveStack()->ID == stackID) | ||||
| @@ -410,6 +418,11 @@ void BattleStacksController::stackRemoved(uint32_t stackID) | ||||
|  | ||||
| void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos) | ||||
| { | ||||
| 	owner.executeOnAnimationCondition(EAnimationEvents::HIT, true, [=](){ | ||||
| 		// remove any potentially erased petrification effect | ||||
| 		removeExpiredColorFilters(); | ||||
| 	}); | ||||
|  | ||||
| 	for(auto & attackedInfo : attackedInfos) | ||||
| 	{ | ||||
| 		if (!attackedInfo.attacker) | ||||
| @@ -466,9 +479,10 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		if (attackedInfo.cloneKilled) | ||||
| 		if (attackedInfo.killed && attackedInfo.defender->summoned) | ||||
| 		{ | ||||
| 			owner.executeOnAnimationCondition(EAnimationEvents::AFTER_HIT, true, [=](){ | ||||
| 				addNewAnim(ColorTransformAnimation::fadeOutAnimation(owner, attackedInfo.defender)); | ||||
| 				stackRemoved(attackedInfo.defender->ID); | ||||
| 			}); | ||||
| 		} | ||||
| @@ -479,8 +493,6 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at | ||||
| void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance) | ||||
| { | ||||
| 	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.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
|  | ||||
| 	if(shouldRotate(stack, stack->getPosition(), destHex[0])) | ||||
| @@ -498,7 +510,6 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH | ||||
|  | ||||
| void BattleStacksController::stackAttacking( const StackAttackInfo & info ) | ||||
| { | ||||
| 	//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 = | ||||
| @@ -566,7 +577,7 @@ void BattleStacksController::stackAttacking( const StackAttackInfo & info ) | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	if (info.spellEffect) | ||||
| 	if (info.spellEffect != SpellID::NONE) | ||||
| 	{ | ||||
| 		owner.executeOnAnimationCondition(EAnimationEvents::HIT, true, [=]() | ||||
| 		{ | ||||
| @@ -626,9 +637,7 @@ 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.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); | ||||
| @@ -644,7 +653,7 @@ void BattleStacksController::endAction(const BattleAction* action) | ||||
| 	} | ||||
| 	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 | ||||
| 	//Ensure that all animation flags were reset | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::OPENING) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false); | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::MOVEMENT) == false); | ||||
| @@ -653,14 +662,23 @@ void BattleStacksController::endAction(const BattleAction* action) | ||||
| 	assert(owner.getAnimationCondition(EAnimationEvents::PROJECTILES) == false); | ||||
|  | ||||
| 	owner.controlPanel->blockUI(activeStack == nullptr); | ||||
| 	removeExpiredColorFilters(); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::startAction(const BattleAction* action) | ||||
| { | ||||
| 	setHoveredStack(nullptr); | ||||
| 	removeExpiredColorFilters(); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::activateStack() | ||||
| void BattleStacksController::stackActivated(const CStack *stack) //TODO: check it all before game state is changed due to abilities | ||||
| { | ||||
| 	stackToActivate = stack; | ||||
| 	owner.waitForAnimationCondition(EAnimationEvents::ACTION, false); | ||||
| 	owner.activateStack(); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::activateStack() //TODO: check it all before game state is changed due to abilities | ||||
| { | ||||
| 	if ( !currentAnimations.empty()) | ||||
| 		return; | ||||
| @@ -765,5 +783,32 @@ Point BattleStacksController::getStackPositionAtHex(BattleHex hexNum, const CSta | ||||
| 	} | ||||
| 	//returning | ||||
| 	return ret + owner.pos.topLeft(); | ||||
|  | ||||
| } | ||||
|  | ||||
| void BattleStacksController::setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell * source, bool persistent) | ||||
| { | ||||
| 	for (auto & filter : stackFilterEffects) | ||||
| 	{ | ||||
| 		if (filter.target == target && filter.source == source) | ||||
| 		{ | ||||
| 			filter.effect     = effect; | ||||
| 			filter.persistent = persistent; | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	stackFilterEffects.push_back({ effect, target, source, persistent }); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::removeExpiredColorFilters() | ||||
| { | ||||
| 	vstd::erase_if(stackFilterEffects, [&](const BattleStackFilterEffect & filter) | ||||
| 	{ | ||||
| 		if (filter.persistent) | ||||
| 			return false; | ||||
| 		if (filter.effect == ColorFilter::genEmptyShifter()) | ||||
| 			return false; | ||||
| 		if (filter.target->hasBonus(Selector::source(Bonus::SPELL_EFFECT, filter.source->id))) | ||||
| 			return false; | ||||
| 		return true; | ||||
| 	}); | ||||
| } | ||||
|   | ||||
| @@ -10,12 +10,14 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../gui/Geometries.h" | ||||
| #include "../gui/ColorFilter.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleHex; | ||||
| class BattleAction; | ||||
| class CStack; | ||||
| class CSpell; | ||||
| class SpellID; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| @@ -23,6 +25,7 @@ VCMI_LIB_NAMESPACE_END | ||||
| struct StackAttackedInfo; | ||||
| struct StackAttackInfo; | ||||
|  | ||||
| class ColorFilter; | ||||
| class Canvas; | ||||
| class BattleInterface; | ||||
| class BattleAnimation; | ||||
| @@ -31,6 +34,14 @@ class BattleAnimation; | ||||
| class BattleRenderer; | ||||
| class IImage; | ||||
|  | ||||
| struct BattleStackFilterEffect | ||||
| { | ||||
| 	ColorFilter effect; | ||||
| 	const CStack * target; | ||||
| 	const CSpell * source; | ||||
| 	bool persistent; | ||||
| }; | ||||
|  | ||||
| /// Class responsible for handling stacks in battle | ||||
| /// Handles ordering of stacks animation | ||||
| /// As well as rendering of stacks, their amount boxes | ||||
| @@ -47,13 +58,16 @@ class BattleStacksController | ||||
| 	/// currently displayed animations <anim, initialized> | ||||
| 	std::vector<BattleAnimation *> currentAnimations; | ||||
|  | ||||
| 	/// currently active color effects on stacks, in order of their addition (which corresponds to their apply order) | ||||
| 	std::vector<BattleStackFilterEffect> stackFilterEffects; | ||||
|  | ||||
| 	/// animations of creatures from fighting armies (order by BattleInfo's stacks' ID) | ||||
| 	std::map<int32_t, std::shared_ptr<CreatureAnimation>> stackAnimation; | ||||
|  | ||||
| 	/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback | ||||
| 	std::map<int, bool> stackFacingRight; | ||||
|  | ||||
| 	/// number of active stack; nullptr - no one | ||||
| 	/// currently active stack; nullptr - no one | ||||
| 	const CStack *activeStack; | ||||
|  | ||||
| 	/// stack below mouse pointer, used for border animation | ||||
| @@ -79,6 +93,7 @@ class BattleStacksController | ||||
| 	std::shared_ptr<IImage> getStackAmountBox(const CStack * stack); | ||||
|  | ||||
| 	void executeAttackAnimations(); | ||||
| 	void removeExpiredColorFilters(); | ||||
| public: | ||||
| 	BattleStacksController(BattleInterface & owner); | ||||
|  | ||||
| @@ -110,6 +125,10 @@ public: | ||||
|  | ||||
| 	void collectRenderableObjects(BattleRenderer & renderer); | ||||
|  | ||||
| 	/// Adds new color filter effect targeting stack | ||||
| 	/// Effect will last as long as stack is affected by specified spell (unless effect is persistent) | ||||
| 	/// If effect from same (target, source) already exists, it will be updated | ||||
| 	void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent); | ||||
| 	void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims | ||||
| 	void updateBattleAnimations(); | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include "../../lib/CCreatureHandler.h" | ||||
|  | ||||
| #include "../gui/Canvas.h" | ||||
| #include "../gui/ColorFilter.h" | ||||
|  | ||||
| static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 }; | ||||
| static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 }; | ||||
| @@ -158,19 +159,6 @@ void CreatureAnimation::setType(ECreatureAnimType::Type type) | ||||
| 	play(); | ||||
| } | ||||
|  | ||||
| void CreatureAnimation::shiftColor(const ColorShifter* shifter) | ||||
| { | ||||
| 	SDL_Color shadowTest = shifter->shiftColor(genShadow(128)); | ||||
|  | ||||
| 	shadowAlpha = shadowTest.a; | ||||
|  | ||||
| 	if(forward) | ||||
| 		forward->shiftColor(shifter); | ||||
|  | ||||
| 	if(reverse) | ||||
| 		reverse->shiftColor(shifter); | ||||
| } | ||||
|  | ||||
| CreatureAnimation::CreatureAnimation(const std::string & name_, TSpeedController controller) | ||||
| 	: name(name_), | ||||
| 	  speed(0.1f), | ||||
| @@ -327,8 +315,11 @@ void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target) | ||||
| 	target[6] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border)); | ||||
| } | ||||
|  | ||||
| void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight) | ||||
| void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight) | ||||
| { | ||||
| 	SDL_Color shadowTest = shifter.shiftColor(genShadow(128)); | ||||
| 	shadowAlpha = shadowTest.a; | ||||
|  | ||||
| 	size_t frame = static_cast<size_t>(floor(currentFrame)); | ||||
|  | ||||
| 	std::shared_ptr<IImage> image; | ||||
| @@ -344,6 +335,7 @@ void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight) | ||||
| 		genSpecialPalette(SpecialPalette); | ||||
|  | ||||
| 		image->setSpecialPallete(SpecialPalette); | ||||
| 		image->adjustPalette(shifter); | ||||
|  | ||||
| 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h)); | ||||
|  | ||||
|   | ||||
| @@ -118,16 +118,13 @@ public: | ||||
| 	/// returns currently rendered type of animation | ||||
| 	ECreatureAnimType::Type getType() const; | ||||
|  | ||||
| 	void nextFrame(Canvas & canvas, bool facingRight); | ||||
| 	void nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight); | ||||
|  | ||||
| 	/// should be called every frame, return true when animation was reset to beginning | ||||
| 	bool incrementFrame(float timePassed); | ||||
|  | ||||
| 	void setBorderColor(SDL_Color palette); | ||||
|  | ||||
| 	/// apply color tint effect | ||||
| 	void shiftColor(const ColorShifter * shifter); | ||||
|  | ||||
| 	/// Gets the current frame ID within current group. | ||||
| 	float getCurrentFrame() const; | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
|  | ||||
| #include "SDL_Extensions.h" | ||||
| #include "SDL_Pixels.h" | ||||
| #include "ColorFilter.h" | ||||
|  | ||||
| #include "../CBitmapHandler.h" | ||||
| #include "../Graphics.h" | ||||
| @@ -105,7 +106,8 @@ public: | ||||
| 	void verticalFlip() override; | ||||
|  | ||||
| 	void shiftPalette(int from, int howMany) override; | ||||
| 	void adjustPalette(const ColorShifter * shifter) override; | ||||
| 	void adjustPalette(const ColorFilter & shifter) override; | ||||
| 	void resetPalette(int colorID) override; | ||||
| 	void resetPalette() override; | ||||
|  | ||||
| 	void setSpecialPallete(const SpecialPalette & SpecialPalette) override; | ||||
| @@ -789,7 +791,7 @@ void SDLImage::shiftPalette(int from, int howMany) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void SDLImage::adjustPalette(const ColorShifter * shifter) | ||||
| void SDLImage::adjustPalette(const ColorFilter & shifter) | ||||
| { | ||||
| 	if(originalPalette == nullptr) | ||||
| 		return; | ||||
| @@ -799,7 +801,7 @@ void SDLImage::adjustPalette(const ColorShifter * shifter) | ||||
| 	// Note: here we skip the first 8 colors in the palette that predefined in H3Palette | ||||
| 	for(int i = 8; i < palette->ncolors; i++) | ||||
| 	{ | ||||
| 		palette->colors[i] = shifter->shiftColor(originalPalette->colors[i]); | ||||
| 		palette->colors[i] = shifter.shiftColor(originalPalette->colors[i]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -812,6 +814,15 @@ void SDLImage::resetPalette() | ||||
| 	SDL_SetPaletteColors(surf->format->palette, originalPalette->colors, 0, originalPalette->ncolors); | ||||
| } | ||||
|  | ||||
| void SDLImage::resetPalette( int colorID ) | ||||
| { | ||||
| 	if(originalPalette == nullptr) | ||||
| 		return; | ||||
|  | ||||
| 	// Always keept the original palette not changed, copy a new palette to assign to surface | ||||
| 	SDL_SetPaletteColors(surf->format->palette, originalPalette->colors + colorID, colorID, 1); | ||||
| } | ||||
|  | ||||
| void SDLImage::setSpecialPallete(const IImage::SpecialPalette & SpecialPalette) | ||||
| { | ||||
| 	if(surf->format->palette) | ||||
| @@ -1079,7 +1090,7 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra | ||||
| 		load(index, targetGroup); | ||||
| } | ||||
|  | ||||
| void CAnimation::shiftColor(const ColorShifter * shifter) | ||||
| void CAnimation::shiftColor(const ColorFilter & shifter) | ||||
| { | ||||
| 	for(auto groupIter = images.begin(); groupIter != images.end(); groupIter++) | ||||
| 	{ | ||||
|   | ||||
| @@ -29,7 +29,7 @@ VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| struct SDL_Surface; | ||||
| class CDefFile; | ||||
| class ColorShifter; | ||||
| class ColorFilter; | ||||
|  | ||||
| /* | ||||
|  * Base class for images, can be used for non-animation pictures as well | ||||
| @@ -62,7 +62,8 @@ public: | ||||
|  | ||||
| 	//only indexed bitmaps, 16 colors maximum | ||||
| 	virtual void shiftPalette(int from, int howMany) = 0; | ||||
| 	virtual void adjustPalette(const ColorShifter * shifter) = 0; | ||||
| 	virtual void adjustPalette(const ColorFilter & shifter) = 0; | ||||
| 	virtual void resetPalette(int colorID) = 0; | ||||
| 	virtual void resetPalette() = 0; | ||||
|  | ||||
| 	//only indexed bitmaps with 7 special colors | ||||
| @@ -122,7 +123,7 @@ public: | ||||
| 	void duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup); | ||||
|  | ||||
| 	// adjust the color of the animation, used in battle spell effects, e.g. Cloned objects | ||||
| 	void shiftColor(const ColorShifter * shifter); | ||||
| 	void shiftColor(const ColorFilter & shifter); | ||||
|  | ||||
| 	//add custom surface to the selected position. | ||||
| 	void setCustom(std::string filename, size_t frame, size_t group=0); | ||||
|   | ||||
| @@ -267,7 +267,7 @@ void CIntObject::addChild(CIntObject * child, bool adjustPosition) | ||||
| 	children.push_back(child); | ||||
| 	child->parent_m = this; | ||||
| 	if(adjustPosition) | ||||
| 		child->pos += pos; | ||||
| 		child->pos += pos.topLeft(); | ||||
|  | ||||
| 	if (!active && child->active) | ||||
| 		child->deactivate(); | ||||
| @@ -289,7 +289,7 @@ void CIntObject::removeChild(CIntObject * child, bool adjustPosition) | ||||
| 	children -= child; | ||||
| 	child->parent_m = nullptr; | ||||
| 	if(adjustPosition) | ||||
| 		child->pos -= pos; | ||||
| 		child->pos -= pos.topLeft(); | ||||
| } | ||||
|  | ||||
| void CIntObject::redraw() | ||||
|   | ||||
							
								
								
									
										115
									
								
								client/gui/ColorFilter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								client/gui/ColorFilter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| /* | ||||
|  * Canvas.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
| #include "ColorFilter.h" | ||||
|  | ||||
| #include <SDL2/SDL_pixels.h> | ||||
|  | ||||
| SDL_Color ColorFilter::shiftColor(const SDL_Color & in) const | ||||
| { | ||||
| 	SDL_Color out; | ||||
| 	out.r = in.r * r.r + in.g * r.g + in.b * r.b + 255 * r.a; | ||||
| 	out.g = in.r * g.r + in.g * g.g + in.b * g.b + 255 * g.a; | ||||
| 	out.b = in.r * b.r + in.g * b.g + in.b * b.b + 255 * b.a; | ||||
| 	out.a = in.a * a; | ||||
| 	return out; | ||||
| } | ||||
|  | ||||
| bool ColorFilter::operator == (const ColorFilter & other) const | ||||
| { | ||||
| 	return | ||||
| 		r.r == other.r.r && r.g && other.r.g && r.b == other.r.b && r.a == other.r.a && | ||||
| 		g.r == other.g.r && g.g && other.g.g && g.b == other.g.b && g.a == other.g.a && | ||||
| 		b.r == other.b.r && b.g && other.b.g && b.b == other.b.b && b.a == other.b.a && | ||||
| 		a == other.a; | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genEmptyShifter( ) | ||||
| { | ||||
| 	return genAlphaShifter( 1.f); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genAlphaShifter( float alpha ) | ||||
| { | ||||
| 	return genMuxerShifter( | ||||
| 				{ 1.f, 0.f, 0.f, 0.f }, | ||||
| 				{ 0.f, 1.f, 0.f, 0.f }, | ||||
| 				{ 0.f, 0.f, 1.f, 0.f }, | ||||
| 				alpha); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genGrayscaleShifter( ) | ||||
| { | ||||
| 	ChannelMuxer gray({0.299f, 0.587f, 0.114f, 0.f}); | ||||
|  | ||||
| 	return genMuxerShifter(gray, gray, gray, 1.f); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genRangeShifter( float minR, float minG, float minB, float maxR, float maxG, float maxB ) | ||||
| { | ||||
| 	return genMuxerShifter( | ||||
| 				{ maxR - minR, 0.f, 0.f, minR }, | ||||
| 				{ 0.f, maxG - minG, 0.f, minG }, | ||||
| 				{ 0.f, 0.f, maxB - minB, minB }, | ||||
| 				  1.f); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a ) | ||||
| { | ||||
| 	return ColorFilter(r, g, b, a); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genInterpolated(const ColorFilter & left, const ColorFilter & right, float power) | ||||
| { | ||||
| 	auto lerpMuxer = [=]( const ChannelMuxer & left, const ChannelMuxer & right ) -> ChannelMuxer | ||||
| 	{ | ||||
| 		return { | ||||
| 			vstd::lerp(left.r, right.r, power), | ||||
| 			vstd::lerp(left.g, right.g, power), | ||||
| 			vstd::lerp(left.b, right.b, power), | ||||
| 			vstd::lerp(left.a, right.a, power) | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	return genMuxerShifter( | ||||
| 		lerpMuxer(left.r, right.r), | ||||
| 		lerpMuxer(left.g, right.g), | ||||
| 		lerpMuxer(left.b, right.b), | ||||
| 		vstd::lerp(left.a, right.a, power) | ||||
| 	); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genCombined(const ColorFilter & left, const ColorFilter & right) | ||||
| { | ||||
| 	// matrix multiplication | ||||
| 	ChannelMuxer r{ | ||||
| 		left.r.r * right.r.r + left.g.r * right.r.g + left.b.r * right.r.b, | ||||
| 		left.r.g * right.r.r + left.g.g * right.r.g + left.b.g * right.r.b, | ||||
| 		left.r.b * right.r.r + left.g.b * right.r.g + left.b.b * right.r.b, | ||||
| 		left.r.a * right.r.r + left.g.a * right.r.g + left.b.a * right.r.b + 1.f * right.r.a, | ||||
| 	}; | ||||
|  | ||||
| 	ChannelMuxer g{ | ||||
| 		left.r.r * right.g.r + left.g.r * right.g.g + left.b.r * right.g.b, | ||||
| 		left.r.g * right.g.r + left.g.g * right.g.g + left.b.g * right.g.b, | ||||
| 		left.r.b * right.g.r + left.g.b * right.g.g + left.b.b * right.g.b, | ||||
| 		left.r.a * right.g.r + left.g.a * right.g.g + left.b.a * right.g.b + 1.f * right.g.a, | ||||
| 	}; | ||||
|  | ||||
| 	ChannelMuxer b{ | ||||
| 		left.r.r * right.b.r + left.g.r * right.b.g + left.b.r * right.b.b, | ||||
| 		left.r.g * right.b.r + left.g.g * right.b.g + left.b.g * right.b.b, | ||||
| 		left.r.b * right.b.r + left.g.b * right.b.g + left.b.b * right.b.b, | ||||
| 		left.r.a * right.b.r + left.g.a * right.b.g + left.b.a * right.b.b + 1.f * right.b.a, | ||||
| 	}; | ||||
|  | ||||
| 	float a = left.a * right.a; | ||||
| 	return genMuxerShifter(r,g,b,a); | ||||
| } | ||||
							
								
								
									
										55
									
								
								client/gui/ColorFilter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								client/gui/ColorFilter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * ColorFilter.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| struct SDL_Color; | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /// Base class for applying palette transformation on images | ||||
| class ColorFilter | ||||
| { | ||||
| 	struct ChannelMuxer { | ||||
| 		float r, g, b, a; | ||||
| 	}; | ||||
|  | ||||
| 	ChannelMuxer r; | ||||
| 	ChannelMuxer g; | ||||
| 	ChannelMuxer b; | ||||
| 	float a; | ||||
|  | ||||
| 	ColorFilter(ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a): | ||||
| 		r(r), g(g), b(b), a(a) | ||||
| 	{} | ||||
| public: | ||||
| 	SDL_Color shiftColor(const SDL_Color & in) const; | ||||
|  | ||||
| 	bool operator == (const ColorFilter & other) const; | ||||
|  | ||||
| 	/// Generates empty object that has no effect on image | ||||
| 	static ColorFilter genEmptyShifter(); | ||||
|  | ||||
| 	/// Generates object that changes alpha (transparency) of the image | ||||
| 	static ColorFilter genAlphaShifter( float alpha ); | ||||
|  | ||||
| 	/// Generates object that applies grayscale effect to image | ||||
| 	static ColorFilter genGrayscaleShifter( ); | ||||
|  | ||||
| 	/// Generates object that transforms each channel independently | ||||
| 	static ColorFilter genRangeShifter( float minR, float minG, float minB, float maxR, float maxG, float maxB ); | ||||
|  | ||||
| 	/// Generates object that performs arbitrary mixing between any channels | ||||
| 	static ColorFilter genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a ); | ||||
|  | ||||
| 	/// Combines 2 mixers into a single object | ||||
| 	static ColorFilter genCombined(const ColorFilter & left, const ColorFilter & right); | ||||
|  | ||||
| 	/// Scales down strength of a shifter to a specified factor | ||||
| 	static ColorFilter genInterpolated(const ColorFilter & left, const ColorFilter & right, float power); | ||||
| }; | ||||
| @@ -11,6 +11,11 @@ | ||||
| #include "Geometries.h" | ||||
| #include "../CMT.h" | ||||
| #include <SDL_events.h> | ||||
| #include "../../lib/int3.h" | ||||
|  | ||||
| Point::Point(const int3 &a) | ||||
| 	:x(a.x),y(a.y) | ||||
| {} | ||||
|  | ||||
| Point::Point(const SDL_MouseMotionEvent &a) | ||||
| 	:x(a.x),y(a.y) | ||||
| @@ -21,7 +26,7 @@ Rect Rect::createCentered( int w, int h ) | ||||
| 	return Rect(screen->w/2 - w/2, screen->h/2 - h/2, w, h); | ||||
| } | ||||
|  | ||||
| Rect Rect::around(const Rect &r, int width) /*creates rect around another */ | ||||
| Rect Rect::around(const Rect &r, int width) | ||||
| { | ||||
| 	return Rect(r.x - width, r.y - width, r.w + width * 2, r.h + width * 2); | ||||
| } | ||||
| @@ -30,3 +35,4 @@ Rect Rect::centerIn(const Rect &r) | ||||
| { | ||||
| 	return Rect(r.x + (r.w - w) / 2, r.y + (r.h - h) / 2, w, h); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -9,13 +9,16 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <SDL_video.h> | ||||
| #include "../../lib/int3.h" | ||||
| #include <SDL2/SDL_rect.h> | ||||
|  | ||||
| enum class ETextAlignment {TOPLEFT, CENTER, BOTTOMRIGHT}; | ||||
|  | ||||
| struct SDL_MouseMotionEvent; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| class int3; | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| // A point with x/y coordinate, used mostly for graphic rendering | ||||
| struct Point | ||||
| { | ||||
| @@ -26,13 +29,14 @@ struct Point | ||||
| 	{ | ||||
| 		x = y = 0; | ||||
| 	}; | ||||
|  | ||||
| 	Point(int X, int Y) | ||||
| 		:x(X),y(Y) | ||||
| 	{}; | ||||
| 	Point(const int3 &a) | ||||
| 		:x(a.x),y(a.y) | ||||
| 	{} | ||||
| 	Point(const SDL_MouseMotionEvent &a); | ||||
|  | ||||
| 	Point(const int3 &a); | ||||
|  | ||||
| 	explicit Point(const SDL_MouseMotionEvent &a); | ||||
|  | ||||
| 	template<typename T> | ||||
| 	Point operator+(const T &b) const | ||||
| @@ -73,10 +77,7 @@ struct Point | ||||
| 		y -= b.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	bool operator<(const Point &b) const //product order | ||||
| 	{ | ||||
| 		return x < b.x   &&   y < b.y; | ||||
| 	} | ||||
|  | ||||
| 	template<typename T> Point& operator=(const T &t) | ||||
| 	{ | ||||
| 		x = t.x; | ||||
| @@ -96,7 +97,7 @@ struct Point | ||||
| /// Rectangle class, which have a position and a size | ||||
| struct Rect : public SDL_Rect | ||||
| { | ||||
| 	Rect()//default c-tor | ||||
| 	Rect() | ||||
| 	{ | ||||
| 		x = y = w = h = -1; | ||||
| 	} | ||||
| @@ -121,42 +122,35 @@ struct Rect : public SDL_Rect | ||||
| 		w = r.w; | ||||
| 		h = r.h; | ||||
| 	} | ||||
| 	Rect(const Rect& r) : Rect(static_cast<const SDL_Rect&>(r)) | ||||
| 	{} | ||||
| 	explicit Rect(const SDL_Surface * const &surf) | ||||
| 	{ | ||||
| 		x = y = 0; | ||||
| 		w = surf->w; | ||||
| 		h = surf->h; | ||||
| 	} | ||||
| 	Rect(const Rect& r) = default; | ||||
|  | ||||
| 	Rect centerIn(const Rect &r); | ||||
| 	static Rect createCentered(int w, int h); | ||||
| 	static Rect around(const Rect &r, int width = 1); //creates rect around another | ||||
| 	static Rect around(const Rect &r, int width = 1); | ||||
|  | ||||
| 	bool isIn(int qx, int qy) const //determines if given point lies inside rect | ||||
| 	bool isIn(int qx, int qy) const | ||||
| 	{ | ||||
| 		if (qx > x   &&   qx<x+w   &&   qy>y   &&   qy<y+h) | ||||
| 			return true; | ||||
| 		return false; | ||||
| 	} | ||||
| 	bool isIn(const Point & q) const //determines if given point lies inside rect | ||||
| 	bool isIn(const Point & q) const | ||||
| 	{ | ||||
| 		return isIn(q.x,q.y); | ||||
| 	} | ||||
| 	Point topLeft() const //top left corner of this rect | ||||
| 	Point topLeft() const | ||||
| 	{ | ||||
| 		return Point(x,y); | ||||
| 	} | ||||
| 	Point topRight() const //top right corner of this rect | ||||
| 	Point topRight() const | ||||
| 	{ | ||||
| 		return Point(x+w,y); | ||||
| 	} | ||||
| 	Point bottomLeft() const //bottom left corner of this rect | ||||
| 	Point bottomLeft() const | ||||
| 	{ | ||||
| 		return Point(x,y+h); | ||||
| 	} | ||||
| 	Point bottomRight() const //bottom right corner of this rect | ||||
| 	Point bottomRight() const | ||||
| 	{ | ||||
| 		return Point(x+w,y+h); | ||||
| 	} | ||||
| @@ -168,21 +162,19 @@ struct Rect : public SDL_Rect | ||||
| 	{ | ||||
| 		return Point(w,h); | ||||
| 	} | ||||
| 	Rect operator+(const Rect &p) const //moves this rect by p's rect position | ||||
|  | ||||
| 	void moveTo(const Point & dest) | ||||
| 	{ | ||||
| 		x = dest.x; | ||||
| 		y = dest.y; | ||||
| 	} | ||||
|  | ||||
| 	Rect operator+(const Point &p) const | ||||
| 	{ | ||||
| 		return Rect(x+p.x,y+p.y,w,h); | ||||
| 	} | ||||
| 	Rect operator+(const Point &p) const //moves this rect by p's point position | ||||
| 	{ | ||||
| 		return Rect(x+p.x,y+p.y,w,h); | ||||
| 	} | ||||
| 	Rect& operator=(const Point &p) //assignment operator | ||||
| 	{ | ||||
| 		x = p.x; | ||||
| 		y = p.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	Rect& operator=(const Rect &p) //assignment operator | ||||
|  | ||||
| 	Rect& operator=(const Rect &p) | ||||
| 	{ | ||||
| 		x = p.x; | ||||
| 		y = p.y; | ||||
| @@ -190,34 +182,21 @@ struct Rect : public SDL_Rect | ||||
| 		h = p.h; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	Rect& operator+=(const Rect &p) //works as operator+ | ||||
|  | ||||
| 	Rect& operator+=(const Point &p) | ||||
| 	{ | ||||
| 		x += p.x; | ||||
| 		y += p.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	Rect& operator+=(const Point &p) //works as operator+ | ||||
| 	{ | ||||
| 		x += p.x; | ||||
| 		y += p.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	Rect& operator-=(const Rect &p) //works as operator+ | ||||
|  | ||||
| 	Rect& operator-=(const Point &p) | ||||
| 	{ | ||||
| 		x -= p.x; | ||||
| 		y -= p.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	Rect& operator-=(const Point &p) //works as operator+ | ||||
| 	{ | ||||
| 		x -= p.x; | ||||
| 		y -= p.y; | ||||
| 		return *this; | ||||
| 	} | ||||
| 	template<typename T> Rect operator-(const T &t) | ||||
| 	{ | ||||
| 		return Rect(x - t.x, y - t.y, w, h); | ||||
| 	} | ||||
|  | ||||
| 	Rect operator&(const Rect &p) const //rect intersection | ||||
| 	{ | ||||
| 		bool intersect = true; | ||||
|   | ||||
| @@ -349,12 +349,12 @@ static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S | ||||
| 	for(int x = x1; x <= x2; x++) | ||||
| 	{ | ||||
| 		float f = float(x - x1) / float(x2 - x1); | ||||
| 		int y = CSDL_Ext::lerp(y1, y2, f); | ||||
| 		int y = vstd::lerp(y1, y2, f); | ||||
|  | ||||
| 		uint8_t r = CSDL_Ext::lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = CSDL_Ext::lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = CSDL_Ext::lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = CSDL_Ext::lerp(color1.a, color2.a, f); | ||||
| 		uint8_t r = vstd::lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = vstd::lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = vstd::lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = vstd::lerp(color1.a, color2.a, f); | ||||
|  | ||||
| 		Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y); | ||||
| 		ColorPutter<4, 0>::PutColor(p, r,g,b,a); | ||||
| @@ -366,12 +366,12 @@ static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S | ||||
| 	for(int y = y1; y <= y2; y++) | ||||
| 	{ | ||||
| 		float f = float(y - y1) / float(y2 - y1); | ||||
| 		int x = CSDL_Ext::lerp(x1, x2, f); | ||||
| 		int x = vstd::lerp(x1, x2, f); | ||||
|  | ||||
| 		uint8_t r = CSDL_Ext::lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = CSDL_Ext::lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = CSDL_Ext::lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = CSDL_Ext::lerp(color1.a, color2.a, f); | ||||
| 		uint8_t r = vstd::lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = vstd::lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = vstd::lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = vstd::lerp(color1.a, color2.a, f); | ||||
|  | ||||
| 		Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y); | ||||
| 		ColorPutter<4, 0>::PutColor(p, r,g,b,a); | ||||
| @@ -922,7 +922,6 @@ void CSDL_Ext::setDefaultColorKeyPresize(SDL_Surface * surface) | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int); | ||||
| template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int); | ||||
| template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int); | ||||
|   | ||||
| @@ -49,12 +49,6 @@ inline bool isShiftKeyDown() | ||||
| } | ||||
| namespace CSDL_Ext | ||||
| { | ||||
| 	template<typename Int> | ||||
| 	Int lerp(Int a, Int b, float f) | ||||
| 	{ | ||||
| 		return a + std::round((b - a) * f); | ||||
| 	} | ||||
|  | ||||
| 	//todo: should this better be assignment operator? | ||||
| 	STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source) | ||||
| 	{ | ||||
| @@ -158,82 +152,6 @@ struct ColorPutter | ||||
|  | ||||
| typedef void (*BlitterWithRotationVal)(SDL_Surface *src,SDL_Rect srcRect, SDL_Surface * dst, SDL_Rect dstRect, ui8 rotation); | ||||
|  | ||||
| /// Base class for applying palette transformation on images | ||||
| class ColorShifter | ||||
| { | ||||
| public: | ||||
| 	~ColorShifter() = default; | ||||
| 	virtual SDL_Color shiftColor(SDL_Color input) const = 0; | ||||
| }; | ||||
|  | ||||
| /// Generic class for palette transformation | ||||
| /// Applies linear transformation to move all colors into range (min, max) | ||||
| class ColorShifterRange : public ColorShifter | ||||
| { | ||||
| 	SDL_Color base; | ||||
| 	SDL_Color factor; | ||||
|  | ||||
| public: | ||||
| 	ColorShifterRange(SDL_Color min, SDL_Color max) : | ||||
| 		base(min) | ||||
| 	{ | ||||
| 		assert(max.r >= min.r); | ||||
| 		assert(max.g >= min.g); | ||||
| 		assert(max.b >= min.b); | ||||
| 		assert(max.a >= min.a); | ||||
| 		factor.r = max.r - min.r; | ||||
| 		factor.g = max.g - min.g; | ||||
| 		factor.b = max.b - min.b; | ||||
| 		factor.a = max.a - min.a; | ||||
| 	} | ||||
|  | ||||
| 	SDL_Color shiftColor(SDL_Color input) const override | ||||
| 	{ | ||||
| 		return { | ||||
| 			uint8_t(base.r + input.r * factor.r / 255), | ||||
| 			uint8_t(base.g + input.g * factor.g / 255), | ||||
| 			uint8_t(base.b + input.b * factor.b / 255), | ||||
| 			uint8_t(base.a + input.a * factor.a / 255), | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| /// Color shifter that allows to specify color to be excempt from changes | ||||
| class ColorShifterRangeExcept : public ColorShifterRange | ||||
| { | ||||
| 	SDL_Color ignored; | ||||
| public: | ||||
| 	ColorShifterRangeExcept(SDL_Color min, SDL_Color max, SDL_Color ignored) : | ||||
| 		ColorShifterRange(min, max), | ||||
| 		ignored(ignored) | ||||
| 	{} | ||||
|  | ||||
| 	SDL_Color shiftColor(SDL_Color input) const override | ||||
| 	{ | ||||
| 		if ( input.r == ignored.r && input.g == ignored.g && input.b == ignored.b && input.a == ignored.a) | ||||
| 			return input; | ||||
| 		return ColorShifterRange::shiftColor(input); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class ColorShifterGrayscale : public ColorShifter | ||||
| { | ||||
| public: | ||||
| 	SDL_Color shiftColor(SDL_Color input) const override | ||||
| 	{ | ||||
| 		// Apply grayscale conversion according to human eye perception values | ||||
| 		uint32_t gray = static_cast<uint32_t>(0.299 * input.r + 0.587 * input.g + 0.114 * input.b); | ||||
| 		assert(gray < 256); | ||||
|  | ||||
| 		return { | ||||
| 			uint8_t(gray), | ||||
| 			uint8_t(gray), | ||||
| 			uint8_t(gray), | ||||
| 			input.a | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| namespace CSDL_Ext | ||||
| { | ||||
| 	/// helper that will safely set and un-set ClipRect for SDL_Surface | ||||
|   | ||||
| @@ -307,7 +307,7 @@ CChatBox::CChatBox(const Rect & rect) | ||||
| 	: CIntObject(KEYBOARD | TEXTINPUT) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION; | ||||
| 	pos += rect; | ||||
| 	pos += rect.topLeft(); | ||||
| 	captureAllKeys = true; | ||||
| 	type |= REDRAW_PARENT; | ||||
|  | ||||
| @@ -341,7 +341,7 @@ void CChatBox::addNewMessage(const std::string & text) | ||||
| CFlagBox::CFlagBox(const Rect & rect) | ||||
| 	: CIntObject(RCLICK) | ||||
| { | ||||
| 	pos += rect; | ||||
| 	pos += rect.topLeft(); | ||||
| 	pos.w = rect.w; | ||||
| 	pos.h = rect.h; | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; | ||||
|   | ||||
| @@ -236,7 +236,7 @@ void CHeroList::CHeroItem::open() | ||||
|  | ||||
| void CHeroList::CHeroItem::showTooltip() | ||||
| { | ||||
| 	CRClickPopup::createAndPush(hero, GH.current->motion); | ||||
| 	CRClickPopup::createAndPush(hero, Point(GH.current->motion)); | ||||
| } | ||||
|  | ||||
| std::string CHeroList::CHeroItem::getHoverText() | ||||
| @@ -328,7 +328,7 @@ void CTownList::CTownItem::open() | ||||
|  | ||||
| void CTownList::CTownItem::showTooltip() | ||||
| { | ||||
| 	CRClickPopup::createAndPush(town, GH.current->motion); | ||||
| 	CRClickPopup::createAndPush(town, Point(GH.current->motion)); | ||||
| } | ||||
|  | ||||
| std::string CTownList::CTownItem::getHoverText() | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "ObjectLists.h" | ||||
| #include "../../lib/FunctionList.h" | ||||
| #include "../../lib/int3.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|   | ||||
| @@ -443,7 +443,7 @@ CComponentBox::CComponentBox(std::vector<std::shared_ptr<CComponent>> _component | ||||
| 	components(_components) | ||||
| { | ||||
| 	type |= REDRAW_PARENT; | ||||
| 	pos = position + pos; | ||||
| 	pos = position + pos.topLeft(); | ||||
| 	placeComponents(false); | ||||
| } | ||||
|  | ||||
| @@ -452,7 +452,7 @@ CComponentBox::CComponentBox(std::vector<std::shared_ptr<CSelectableComponent>> | ||||
| 	onSelect(_onSelect) | ||||
| { | ||||
| 	type |= REDRAW_PARENT; | ||||
| 	pos = position + pos; | ||||
| 	pos = position + pos.topLeft(); | ||||
| 	placeComponents(true); | ||||
|  | ||||
| 	assert(!components.empty()); | ||||
|   | ||||
| @@ -167,7 +167,7 @@ void CPicture::scaleTo(Point size) | ||||
|  | ||||
| void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color) | ||||
| { | ||||
| 	pos += r; | ||||
| 	pos += r.topLeft(); | ||||
| 	pos.w = r.w; | ||||
| 	pos.h = r.h; | ||||
| 	if(screenFormat) | ||||
|   | ||||
| @@ -68,7 +68,7 @@ LRClickableAreaWText::LRClickableAreaWText() | ||||
| LRClickableAreaWText::LRClickableAreaWText(const Rect &Pos, const std::string &HoverText, const std::string &ClickText) | ||||
| { | ||||
| 	init(); | ||||
| 	pos = Pos + pos; | ||||
| 	pos = Pos + pos.topLeft(); | ||||
| 	hoverText = HoverText; | ||||
| 	text = ClickText; | ||||
| } | ||||
| @@ -430,7 +430,7 @@ MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small) | ||||
| 	small(Small) | ||||
| { | ||||
| 	bonusValue = 0; | ||||
| 	pos = r + pos; | ||||
| 	pos = r + pos.topLeft(); | ||||
| 	defActions = 255-DISPOSE; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -473,7 +473,7 @@ CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(c | ||||
| CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const std::string & bgName, const CFunctionList<void(const std::string &)> & CB) | ||||
| 	:cb(CB), 	CFocusable(std::make_shared<CKeyboardFocusListener>(this)) | ||||
| { | ||||
| 	pos += Pos; | ||||
| 	pos += Pos.topLeft(); | ||||
| 	pos.h = Pos.h; | ||||
| 	pos.w = Pos.w; | ||||
|  | ||||
| @@ -490,7 +490,7 @@ CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const std::stri | ||||
| CTextInput::CTextInput(const Rect & Pos, SDL_Surface * srf) | ||||
| 	:CFocusable(std::make_shared<CKeyboardFocusListener>(this)) | ||||
| { | ||||
| 	pos += Pos; | ||||
| 	pos += Pos.topLeft(); | ||||
| 	captureAllKeys = true; | ||||
| 	OBJ_CONSTRUCTION; | ||||
| 	background = std::make_shared<CPicture>(Pos, 0, true); | ||||
|   | ||||
| @@ -1810,7 +1810,7 @@ void CAdvMapInt::tileRClicked(const int3 &mapPos) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	CRClickPopup::createAndPush(obj, GH.current->motion, ETextAlignment::CENTER); | ||||
| 	CRClickPopup::createAndPush(obj, Point(GH.current->motion), ETextAlignment::CENTER); | ||||
| } | ||||
|  | ||||
| void CAdvMapInt::enterCastingMode(const CSpell * sp) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user