mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implemented bloodlust, clone, and petrify effects for xbrz mode
This commit is contained in:
		| @@ -594,16 +594,19 @@ void ColorTransformAnimation::tick(uint32_t msPassed) | ||||
| 	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); | ||||
| 		const auto & lastColor = effectColors[index - 1]; | ||||
| 		const auto & lastAlpha = transparency[index - 1]; | ||||
| 		owner.stacksController->setStackColorFilter(lastColor, lastAlpha, stack, spell, false); | ||||
| 		delete this; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	assert(index != 0); | ||||
|  | ||||
| 	const auto & prevShifter = steps[index - 1]; | ||||
| 	const auto & nextShifter = steps[index]; | ||||
| 	const auto & prevColor = effectColors[index - 1]; | ||||
| 	const auto & nextColor = effectColors[index]; | ||||
| 	const auto & prevAlpha = transparency[index - 1]; | ||||
| 	const auto & nextAlpha = transparency[index]; | ||||
|  | ||||
| 	float prevPoint = timePoints[index-1]; | ||||
| 	float nextPoint = timePoints[index]; | ||||
| @@ -611,9 +614,10 @@ void ColorTransformAnimation::tick(uint32_t msPassed) | ||||
| 	float stepDuration = (nextPoint - prevPoint); | ||||
| 	float factor = localProgress / stepDuration; | ||||
|  | ||||
| 	auto shifter = ColorFilter::genInterpolated(prevShifter, nextShifter, factor); | ||||
| 	const auto & currColor = vstd::lerp(prevColor, nextColor, factor); | ||||
| 	const auto & currAlpha = vstd::lerp(prevAlpha, nextAlpha, factor); | ||||
|  | ||||
| 	owner.stacksController->setStackColorFilter(shifter, stack, spell, true); | ||||
| 	owner.stacksController->setStackColorFilter(currColor, currAlpha, stack, spell, true); | ||||
| } | ||||
|  | ||||
| ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell): | ||||
| @@ -622,10 +626,11 @@ ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const | ||||
| 	totalProgress(0.f) | ||||
| { | ||||
| 	auto effect = owner.effectsController->getMuxerEffect(colorFilterName); | ||||
| 	steps = effect.filters; | ||||
| 	effectColors = effect.effectColors; | ||||
| 	transparency = effect.transparency; | ||||
| 	timePoints = effect.timePoints; | ||||
|  | ||||
| 	assert(!steps.empty() && steps.size() == timePoints.size()); | ||||
| 	assert(!effectColors.empty() && effectColors.size() == timePoints.size()); | ||||
|  | ||||
| 	logAnim->debug("Created ColorTransformAnimation for %s", stack->getName()); | ||||
| } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "../../lib/battle/BattleHexArray.h" | ||||
| #include "../../lib/filesystem/ResourcePath.h" | ||||
| #include "../../lib/Color.h" | ||||
| #include "BattleConstants.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| @@ -113,8 +114,10 @@ public: | ||||
|  | ||||
| class ColorTransformAnimation : public BattleStackAnimation | ||||
| { | ||||
| 	std::vector<ColorFilter> steps; | ||||
| 	std::vector<ColorRGBA> effectColors; | ||||
| 	std::vector<float> transparency; | ||||
| 	std::vector<float> timePoints; | ||||
|  | ||||
| 	const CSpell * spell; | ||||
|  | ||||
| 	float totalProgress; | ||||
|   | ||||
| @@ -143,7 +143,8 @@ void BattleEffectsController::loadColorMuxers() | ||||
| 		for (const JsonNode & entry : muxer.second.Vector() ) | ||||
| 		{ | ||||
| 			effect.timePoints.push_back(entry["time"].Float()); | ||||
| 			effect.filters.push_back(ColorFilter::genFromJson(entry)); | ||||
| 			effect.effectColors.push_back(ColorRGBA(255*entry["color"][0].Float(), 255*entry["color"][1].Float(), 255*entry["color"][2].Float(), 255*entry["color"][3].Float())); | ||||
| 			effect.transparency.push_back(entry["alpha"].Float() * 255); | ||||
| 		} | ||||
| 		colorMuxerEffects[identifier] = effect; | ||||
| 	} | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "../../lib/battle/BattleHex.h" | ||||
| #include "../../lib/Point.h" | ||||
| #include "../../lib/Color.h" | ||||
| #include "../../lib/filesystem/ResourcePath.h" | ||||
| #include "BattleConstants.h" | ||||
|  | ||||
| @@ -21,13 +22,19 @@ struct BattleTriggerEffect; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| struct ColorMuxerEffect; | ||||
| class CAnimation; | ||||
| class Canvas; | ||||
| class BattleInterface; | ||||
| class BattleRenderer; | ||||
| class EffectAnimation; | ||||
|  | ||||
| struct ColorMuxerEffect | ||||
| { | ||||
| 	std::vector<ColorRGBA> effectColors; | ||||
| 	std::vector<float> transparency; | ||||
| 	std::vector<float> timePoints; | ||||
| }; | ||||
|  | ||||
| /// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,... | ||||
| struct BattleEffect | ||||
| { | ||||
|   | ||||
| @@ -204,8 +204,7 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant) | ||||
| 	if (!instant) | ||||
| 	{ | ||||
| 		// immediately make stack transparent, giving correct shifter time to start | ||||
| 		auto shifterFade = ColorFilter::genAlphaShifter(0); | ||||
| 		setStackColorFilter(shifterFade, stack, nullptr, true); | ||||
| 		setStackColorFilter(Colors::TRANSPARENCY, 0, stack, nullptr, true); | ||||
|  | ||||
| 		owner.addToAnimationStage(EAnimationEvents::HIT, [=]() | ||||
| 		{ | ||||
| @@ -324,14 +323,18 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack * | ||||
|  | ||||
| void BattleStacksController::showStack(Canvas & canvas, const CStack * stack) | ||||
| { | ||||
| 	ColorFilter fullFilter = ColorFilter::genEmptyShifter(); | ||||
| 	ColorRGBA effectColor = Colors::TRANSPARENCY; | ||||
| 	uint8_t transparency = 255; | ||||
| 	for(const auto & filter : stackFilterEffects) | ||||
| 	{ | ||||
| 		if (filter.target == stack) | ||||
| 			fullFilter = ColorFilter::genCombined(fullFilter, filter.effect); | ||||
| 		{ | ||||
| 			effectColor = filter.effectColor; | ||||
| 			transparency = static_cast<int>(filter.transparency) * transparency / 255; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit | ||||
| 	stackAnimation[stack->unitId()]->nextFrame(canvas, effectColor, transparency, facingRight(stack)); // do actual blit | ||||
| } | ||||
|  | ||||
| void BattleStacksController::tick(uint32_t msPassed) | ||||
| @@ -769,18 +772,19 @@ Point BattleStacksController::getStackPositionAtHex(const BattleHex & hexNum, co | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void BattleStacksController::setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell * source, bool persistent) | ||||
| void BattleStacksController::setStackColorFilter(const ColorRGBA & effectColor, uint8_t transparency, const CStack * target, const CSpell * source, bool persistent) | ||||
| { | ||||
| 	for (auto & filter : stackFilterEffects) | ||||
| 	{ | ||||
| 		if (filter.target == target && filter.source == source) | ||||
| 		{ | ||||
| 			filter.effect     = effect; | ||||
| 			filter.effectColor = effectColor; | ||||
| 			filter.transparency	= transparency; | ||||
| 			filter.persistent = persistent; | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	stackFilterEffects.push_back({ effect, target, source, persistent }); | ||||
| 	stackFilterEffects.push_back({ target, source, effectColor, transparency, persistent }); | ||||
| } | ||||
|  | ||||
| void BattleStacksController::removeExpiredColorFilters() | ||||
| @@ -791,7 +795,7 @@ void BattleStacksController::removeExpiredColorFilters() | ||||
| 		{ | ||||
| 			if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(filter.source->id)), Selector::all)) | ||||
| 				return true; | ||||
| 			if (filter.effect == ColorFilter::genEmptyShifter()) | ||||
| 			if (filter.effectColor == Colors::TRANSPARENCY && filter.transparency == 255) | ||||
| 				return true; | ||||
| 		} | ||||
| 		return false; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../render/ColorFilter.h" | ||||
| #include "../../lib/Color.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -37,9 +37,10 @@ class IImage; | ||||
|  | ||||
| struct BattleStackFilterEffect | ||||
| { | ||||
| 	ColorFilter effect; | ||||
| 	const CStack * target; | ||||
| 	const CSpell * source; | ||||
| 	ColorRGBA effectColor; | ||||
| 	uint8_t transparency; | ||||
| 	bool persistent; | ||||
| }; | ||||
|  | ||||
| @@ -134,7 +135,7 @@ public: | ||||
| 	/// 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 setStackColorFilter(const ColorRGBA & effect, uint8_t transparency, const CStack * target, const CSpell *source, bool persistent); | ||||
| 	void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims | ||||
|  | ||||
| 	const CStack* getActiveStack() const; | ||||
|   | ||||
| @@ -24,11 +24,6 @@ static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 }; | ||||
| static const ColorRGBA creatureGoldBorder = { 255, 255, 0, 255 }; | ||||
| static const ColorRGBA creatureNoBorder  =  { 0, 0, 0, 0 }; | ||||
|  | ||||
| static ColorRGBA genShadow(ui8 alpha) | ||||
| { | ||||
| 	return ColorRGBA(0, 0, 0, alpha); | ||||
| } | ||||
|  | ||||
| ColorRGBA AnimationControls::getBlueBorder() | ||||
| { | ||||
| 	return creatureBlueBorder; | ||||
| @@ -192,7 +187,6 @@ void CreatureAnimation::setType(ECreatureAnimType type) | ||||
| CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedController controller) | ||||
| 	: name(name_), | ||||
| 	  speed(0.1f), | ||||
| 	  shadowAlpha(128), | ||||
| 	  currentFrame(0), | ||||
| 	  animationEnd(-1), | ||||
| 	  elapsedTime(0), | ||||
| @@ -324,11 +318,8 @@ static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base) | ||||
| 	return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256)); | ||||
| } | ||||
|  | ||||
| void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight) | ||||
| void CreatureAnimation::nextFrame(Canvas & canvas, const ColorRGBA & effectColor, uint8_t transparency, bool facingRight) | ||||
| { | ||||
| 	ColorRGBA shadowTest = shifter.shiftColor(genShadow(128)); | ||||
| 	shadowAlpha = shadowTest.a; | ||||
|  | ||||
| 	size_t frame = static_cast<size_t>(floor(currentFrame)); | ||||
|  | ||||
| 	std::shared_ptr<IImage> image; | ||||
| @@ -345,7 +336,8 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, | ||||
| 		else | ||||
| 			image->setOverlayColor(Colors::TRANSPARENCY); | ||||
|  | ||||
| 		image->adjustPalette(shifter, 0); | ||||
| 		image->setEffectColor(effectColor); | ||||
| 		image->setAlpha(transparency); | ||||
|  | ||||
| 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h)); | ||||
| 	} | ||||
|   | ||||
| @@ -94,9 +94,6 @@ private: | ||||
| 	///type of animation being displayed | ||||
| 	ECreatureAnimType type; | ||||
|  | ||||
| 	/// current value of shadow transparency | ||||
| 	uint8_t shadowAlpha; | ||||
|  | ||||
| 	/// border color, disabled if alpha = 0 | ||||
| 	ColorRGBA border; | ||||
|  | ||||
| @@ -127,7 +124,7 @@ public: | ||||
| 	/// returns currently rendered type of animation | ||||
| 	ECreatureAnimType getType() const; | ||||
|  | ||||
| 	void nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight); | ||||
| 	void nextFrame(Canvas & canvas, const ColorRGBA & effectColor, uint8_t transparency, bool facingRight); | ||||
|  | ||||
| 	/// should be called every frame, return true when animation was reset to beginning | ||||
| 	bool incrementFrame(float timePassed); | ||||
|   | ||||
| @@ -32,6 +32,7 @@ public: | ||||
| 	void setAlpha(uint8_t value) override{}; | ||||
| 	void playerColored(const PlayerColor & player) override{}; | ||||
| 	void setOverlayColor(const ColorRGBA & color) override{}; | ||||
| 	void setEffectColor(const ColorRGBA & color) override{}; | ||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override{}; | ||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override{}; | ||||
|  | ||||
|   | ||||
| @@ -129,40 +129,3 @@ ColorFilter ColorFilter::genCombined(const ColorFilter & left, const ColorFilter | ||||
| 	float a = left.a * right.a; | ||||
| 	return genMuxerShifter(r,g,b,a); | ||||
| } | ||||
|  | ||||
| ColorFilter ColorFilter::genFromJson(const JsonNode & entry) | ||||
| { | ||||
| 	ChannelMuxer r{ 1.f, 0.f, 0.f, 0.f }; | ||||
| 	ChannelMuxer g{ 0.f, 1.f, 0.f, 0.f }; | ||||
| 	ChannelMuxer b{ 0.f, 0.f, 1.f, 0.f }; | ||||
| 	float a{ 1.0}; | ||||
|  | ||||
| 	if (!entry["red"].isNull()) | ||||
| 	{ | ||||
| 		r.r = entry["red"].Vector()[0].Float(); | ||||
| 		r.g = entry["red"].Vector()[1].Float(); | ||||
| 		r.b = entry["red"].Vector()[2].Float(); | ||||
| 		r.a = entry["red"].Vector()[3].Float(); | ||||
| 	} | ||||
|  | ||||
| 	if (!entry["green"].isNull()) | ||||
| 	{ | ||||
| 		g.r = entry["green"].Vector()[0].Float(); | ||||
| 		g.g = entry["green"].Vector()[1].Float(); | ||||
| 		g.b = entry["green"].Vector()[2].Float(); | ||||
| 		g.a = entry["green"].Vector()[3].Float(); | ||||
| 	} | ||||
|  | ||||
| 	if (!entry["blue"].isNull()) | ||||
| 	{ | ||||
| 		b.r = entry["blue"].Vector()[0].Float(); | ||||
| 		b.g = entry["blue"].Vector()[1].Float(); | ||||
| 		b.b = entry["blue"].Vector()[2].Float(); | ||||
| 		b.a = entry["blue"].Vector()[3].Float(); | ||||
| 	} | ||||
|  | ||||
| 	if (!entry["alpha"].isNull()) | ||||
| 		a = entry["alpha"].Float(); | ||||
|  | ||||
| 	return genMuxerShifter(r,g,b,a); | ||||
| } | ||||
|   | ||||
| @@ -54,13 +54,4 @@ public: | ||||
|  | ||||
| 	/// Scales down strength of a shifter to a specified factor | ||||
| 	static ColorFilter genInterpolated(const ColorFilter & left, const ColorFilter & right, float power); | ||||
|  | ||||
| 	/// Generates object using supplied Json config | ||||
| 	static ColorFilter genFromJson(const JsonNode & entry); | ||||
| }; | ||||
|  | ||||
| struct ColorMuxerEffect | ||||
| { | ||||
| 	std::vector<ColorFilter> filters; | ||||
| 	std::vector<float> timePoints; | ||||
| }; | ||||
|   | ||||
| @@ -51,6 +51,7 @@ enum class EImageBlitMode : uint8_t | ||||
| 	WITH_SHADOW_AND_FLAG_COLOR, | ||||
|  | ||||
| 	/// RGBA, contains only body, with shadow and overlay disabled | ||||
| 	GRAYSCALE_BODY_HIDE_SELECTION, | ||||
| 	ONLY_BODY_HIDE_SELECTION, | ||||
| 	ONLY_BODY_HIDE_FLAG_COLOR, | ||||
|  | ||||
| @@ -104,8 +105,8 @@ public: | ||||
|  | ||||
| 	virtual void setAlpha(uint8_t value) = 0; | ||||
|  | ||||
| 	//only indexed bitmaps with 7 special colors | ||||
| 	virtual void setOverlayColor(const ColorRGBA & color) = 0; | ||||
| 	virtual void setEffectColor(const ColorRGBA & color) = 0; | ||||
|  | ||||
| 	virtual ~IImage() = default; | ||||
| }; | ||||
|   | ||||
| @@ -103,6 +103,10 @@ void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette | ||||
| 		case EImageBlitMode::ONLY_SELECTION: | ||||
| 			adjustPalette(originalPalette, blitMode, ColorFilter::genAlphaShifter(0), 0); | ||||
| 			break; | ||||
| 		case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION: | ||||
| 			adjustPalette(originalPalette, blitMode, ColorFilter::genMuxerShifter( { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, 1), 0); | ||||
| 			break; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	switch(blitMode) | ||||
| @@ -120,6 +124,7 @@ void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette | ||||
| 		case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: | ||||
| 		case EImageBlitMode::ONLY_FLAG_COLOR: | ||||
| 		case EImageBlitMode::ONLY_SELECTION: | ||||
| 		case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION: | ||||
| 			setShadowTransparency(originalPalette, 0.0); | ||||
| 			break; | ||||
| 	} | ||||
| @@ -140,6 +145,7 @@ void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette | ||||
| 			break; | ||||
| 		case EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION: | ||||
| 		case EImageBlitMode::ONLY_BODY_HIDE_SELECTION: | ||||
| 		case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION: | ||||
| 			setOverlayColor(originalPalette, Colors::TRANSPARENCY, true); | ||||
| 			break; | ||||
| 	} | ||||
| @@ -273,11 +279,15 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re | ||||
| 	bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading(); | ||||
| 	bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading(); | ||||
| 	bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading(); | ||||
| 	bool grayscaleLoading = scaled.at(scalingFactor).bodyGrayscale.at(0) && scaled.at(scalingFactor).bodyGrayscale.at(0)->isLoading(); | ||||
| 	bool playerLoading = parameters.player != PlayerColor::CANNOT_DETERMINE && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())->isLoading(); | ||||
|  | ||||
| 	if (shadowLoading || bodyLoading || overlayLoading || playerLoading) | ||||
| 	if (shadowLoading || bodyLoading || overlayLoading || playerLoading || grayscaleLoading) | ||||
| 	{ | ||||
| 		getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer); | ||||
|  | ||||
| 		if (parameters.effectColorMultiplier.a != ColorRGBA::ALPHA_TRANSPARENT) | ||||
| 			getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.effectColorMultiplier, parameters.alphaValue, locator.layer); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -292,6 +302,9 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re | ||||
| 	{ | ||||
| 		if (scaled.at(scalingFactor).body.at(0)) | ||||
| 			flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue); | ||||
|  | ||||
| 		if (scaled.at(scalingFactor).bodyGrayscale.at(0) && parameters.effectColorMultiplier.a != ColorRGBA::ALPHA_TRANSPARENT) | ||||
| 			flipAndDraw(scaled.at(scalingFactor).bodyGrayscale, parameters.effectColorMultiplier, parameters.alphaValue); | ||||
| 	} | ||||
|  | ||||
| 	if (scaled.at(scalingFactor).overlay.at(0)) | ||||
| @@ -370,6 +383,24 @@ void ScalableImageInstance::setOverlayColor(const ColorRGBA & color) | ||||
| 		parameters.setOverlayColor(image->getPalette(), color, blitMode == EImageBlitMode::WITH_SHADOW_AND_SELECTION); | ||||
| } | ||||
|  | ||||
| void ScalableImageInstance::setEffectColor(const ColorRGBA & color) | ||||
| { | ||||
| 	parameters.effectColorMultiplier = color; | ||||
|  | ||||
| 	if (parameters.palette) | ||||
| 	{ | ||||
| 		const auto grayscaleFilter = ColorFilter::genMuxerShifter( { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, 1); | ||||
| 		const auto effectStrengthFilter = ColorFilter::genRangeShifter( 0, 0, 0, color.r / 255.f, color.g / 255.f, color.b / 255.f); | ||||
| 		const auto effectFilter = ColorFilter::genCombined(grayscaleFilter, effectStrengthFilter); | ||||
| 		const auto effectiveFilter = ColorFilter::genInterpolated(ColorFilter::genEmptyShifter(), effectFilter, color.a / 255.f); | ||||
|  | ||||
| 		parameters.adjustPalette(image->getPalette(), blitMode, effectiveFilter, 0); | ||||
| 	} | ||||
|  | ||||
| 	if (color.a != ColorRGBA::ALPHA_TRANSPARENT) | ||||
| 		image->prepareEffectImage(); | ||||
| } | ||||
|  | ||||
| void ScalableImageInstance::playerColored(const PlayerColor & player) | ||||
| { | ||||
| 	parameters.player = player; | ||||
| @@ -424,11 +455,19 @@ std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EIm | ||||
| 	if (loadedImage) | ||||
| 		return loadedImage; | ||||
|  | ||||
| 	// optional images for 1x resolution - only try load them, don't attempt to generate | ||||
| 	bool optionalImage = | ||||
| 		mode == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR || | ||||
| 		mode == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION || | ||||
| 		mode == EImageBlitMode::ONLY_FLAG_COLOR || | ||||
| 		mode == EImageBlitMode::ONLY_SELECTION || | ||||
| 		mode == EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION || | ||||
| 		color != PlayerColor::CANNOT_DETERMINE; | ||||
|  | ||||
| 	if (scalingFactor == 1) | ||||
| 	{ | ||||
| 		// optional images for 1x resolution - only try load them, don't attempt to generate | ||||
| 		// this block should never be called for 'body' layer - that image is loaded unconditionally before construction | ||||
| 		assert(mode == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR || mode == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION || mode == EImageBlitMode::ONLY_FLAG_COLOR || mode == EImageBlitMode::ONLY_SELECTION || color != PlayerColor::CANNOT_DETERMINE); | ||||
| 		assert(optionalImage); | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| @@ -441,7 +480,7 @@ std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EIm | ||||
| 		{ | ||||
| 			if (scaling == 1) | ||||
| 			{ | ||||
| 				if (mode == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR || mode == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION || mode == EImageBlitMode::ONLY_FLAG_COLOR || mode == EImageBlitMode::ONLY_SELECTION || color != PlayerColor::CANNOT_DETERMINE) | ||||
| 				if (optionalImage) | ||||
| 				{ | ||||
| 					ScalableImageParameters parameters(getPalette(), mode); | ||||
| 					return loadedImage->scaleInteger(scalingFactor, parameters.palette, mode); | ||||
| @@ -560,3 +599,20 @@ void ScalableImageShared::preparePlayerColoredImage(PlayerColor color) | ||||
| { | ||||
| 	loadScaledImages(GH.screenHandler().getScalingFactor(), color); | ||||
| } | ||||
|  | ||||
| void ScalableImageShared::prepareEffectImage() | ||||
| { | ||||
| 	int scalingFactor = GH.screenHandler().getScalingFactor(); | ||||
|  | ||||
| 	if (scaled[scalingFactor].bodyGrayscale[0] == nullptr) | ||||
| 	{ | ||||
| 		switch(locator.layer) | ||||
| 		{ | ||||
| 			case EImageBlitMode::WITH_SHADOW_AND_SELECTION: | ||||
| 				scaled[scalingFactor].bodyGrayscale[0] = loadOrGenerateImage(EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].bodyGrayscale[0]); | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -26,6 +26,7 @@ struct ScalableImageParameters : boost::noncopyable | ||||
|  | ||||
| 	ColorRGBA colorMultiplier = Colors::WHITE_TRUE; | ||||
| 	ColorRGBA ovelayColorMultiplier = Colors::WHITE_TRUE; | ||||
| 	ColorRGBA effectColorMultiplier = Colors::TRANSPARENCY; | ||||
|  | ||||
| 	PlayerColor player = PlayerColor::CANNOT_DETERMINE; | ||||
| 	uint8_t alphaValue = 255; | ||||
| @@ -64,6 +65,9 @@ class ScalableImageShared final : public std::enable_shared_from_this<ScalableIm | ||||
| 		/// Upscaled overlay (player color, selection highlight) of our image, may be null | ||||
| 		FlippedImages overlay; | ||||
|  | ||||
| 		/// Upscaled grayscale version of body, for special effects in combat (e.g clone / petrify / berserk) | ||||
| 		FlippedImages bodyGrayscale; | ||||
|  | ||||
| 		/// player-colored images of this particular scale, mostly for UI. These are never flipped in h3 | ||||
| 		PlayerColoredImages playerColored; | ||||
| 	}; | ||||
| @@ -91,6 +95,7 @@ public: | ||||
|  | ||||
| 	std::shared_ptr<ScalableImageInstance> createImageReference(); | ||||
|  | ||||
| 	void prepareEffectImage(); | ||||
| 	void preparePlayerColoredImage(PlayerColor color); | ||||
| }; | ||||
|  | ||||
| @@ -115,6 +120,7 @@ public: | ||||
| 	void setAlpha(uint8_t value) override; | ||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override; | ||||
| 	void setOverlayColor(const ColorRGBA & color) override; | ||||
| 	void setEffectColor(const ColorRGBA & color) override; | ||||
| 	void playerColored(const PlayerColor & player) override; | ||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | ||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | ||||
|   | ||||
| @@ -10,12 +10,8 @@ | ||||
| 				// Note that actual speed of animation is subject to changes from in-game animation speed setting | ||||
| 				"time" : 0.0, | ||||
|  | ||||
| 				// Optional. Transformation filter for red, green and blue components of a color | ||||
| 				// Applies transformation with specified parameters to each channel. Formula: | ||||
| 				// result = red * (value 1) + green * (value 2) + blue * (value 3) + (value 4) | ||||
| 				"red"   : [ 1.0, 0.0, 0.0, 0.0 ], | ||||
| 				"green" : [ 0.0, 1.0, 0.0, 0.0 ], | ||||
| 				"blue"  : [ 0.0, 0.0, 1.0, 0.0 ], | ||||
| 				// Optional. Adds grayscale overlay on top of creature, multiplied by specified color per channel (rgba order) | ||||
| 				"color"   : [ 1.0, 0.0, 0.0, 1.0 ], | ||||
| 				 | ||||
| 				/// Optional. Transparency filter, makes stack appear semi-transparent, used mostly for fade-in effects | ||||
| 				/// Value 0 = full transparency, 1 = fully opaque | ||||
| @@ -25,23 +21,21 @@ | ||||
| 	 | ||||
| 		"petrification" : [ | ||||
| 			{ | ||||
| 				"time" : 0.0 | ||||
| 				"time" : 0.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time"  : 1.0, | ||||
| 				// Conversion to grayscale, using human eye perception factor for channels | ||||
| 				"red"   : [ 0.299, 0.587, 0.114, 0.0 ], | ||||
| 				"green" : [ 0.299, 0.587, 0.114, 0.0 ], | ||||
| 				"blue"  : [ 0.299, 0.587, 0.114, 0.0 ], | ||||
| 				"color"  : [ 1.0, 1.0, 1.0, 1.0 ], //grayscale | ||||
| 				"alpha" : 1.0 | ||||
| 			} | ||||
| 		], | ||||
| 		"cloning" : [ | ||||
| 			{  | ||||
| 				// No fade in - will be handled by summonFadeIn effect | ||||
| 				"time" : 0.0,  | ||||
| 				"red"   : [ 0.5, 0.0, 0.0, 0.0 ], | ||||
| 				"green" : [ 0.0, 0.5, 0.0, 0.0 ], | ||||
| 				"blue"  : [ 0.0, 0.0, 0.5, 0.5 ], | ||||
| 				"color"  : [ 0.0, 0.0, 1.0, 1.0 ], //blue | ||||
| 				"alpha" : 1.0 | ||||
| 			} | ||||
| 		], | ||||
| 		"summonFadeIn" : [ | ||||
| @@ -50,12 +44,14 @@ | ||||
| 				"alpha" : 0.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time" : 1.0 | ||||
| 				"time" : 1.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 		], | ||||
| 		"summonFadeOut" : [ | ||||
| 			{ | ||||
| 				"time" : 0.0 | ||||
| 				"time" : 0.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time" : 1.0, | ||||
| @@ -68,12 +64,14 @@ | ||||
| 				"alpha" : 0.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time" : 0.5 | ||||
| 				"time" : 0.5, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 		], | ||||
| 		"teleportFadeOut" : [ | ||||
| 			{ | ||||
| 				"time" : 0.0 | ||||
| 				"time" : 0.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time" : 0.5, | ||||
| @@ -82,31 +80,17 @@ | ||||
| 		], | ||||
| 		"bloodlust" : [ | ||||
| 			{ | ||||
| 				"time" : 0.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time"  : 0.25,  | ||||
| 				"red"   : [ 0.5, 0.0, 0.5, 0.4 ], | ||||
| 				"green" : [ 0.0, 1.0, 0.0, 0.0 ], | ||||
| 				"blue"  : [ 0.0, 0.0, 1.0, 0.0 ], | ||||
| 				"time" : 0.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time"  : 0.5, | ||||
| 				"red"   : [ 0.6, 0.6, 0.6, 0.0 ], | ||||
| 				"green" : [ 0.0, 0.5, 0.0, 0.0 ], | ||||
| 				"blue"  : [ 0.0, 0.0, 0.5, 0.0 ], | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time"  : 0.75,  | ||||
| 				"red"   : [ 0.5, 0.0, 0.5, 0.4 ], | ||||
| 				"green" : [ 0.0, 1.0, 0.0, 0.0 ], | ||||
| 				"blue"  : [ 0.0, 0.0, 1.0, 0.0 ], | ||||
| 				"color"  : [ 1.0, 0.0, 0.0, 1.0 ], //red | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 			{ | ||||
| 				"time" : 1.0, | ||||
| 				"alpha" : 1.0 | ||||
| 			}, | ||||
| 		], | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										14
									
								
								lib/Color.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/Color.h
									
									
									
									
									
								
							| @@ -64,4 +64,18 @@ public: | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| template<typename Floating> | ||||
| ColorRGBA lerp(const ColorRGBA & left, const ColorRGBA & right, const Floating & factor) | ||||
| { | ||||
| 	return ColorRGBA( | ||||
| 		vstd::lerp(left.r, right.r, factor), | ||||
| 		vstd::lerp(left.g, right.g, factor), | ||||
| 		vstd::lerp(left.b, right.b, factor), | ||||
| 		vstd::lerp(left.a, right.a, factor) | ||||
| 	); | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
		Reference in New Issue
	
	Block a user