diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index 196066d1f..b5e174b77 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -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()); } diff --git a/client/battle/BattleAnimationClasses.h b/client/battle/BattleAnimationClasses.h index 1314c2350..ff501cbfa 100644 --- a/client/battle/BattleAnimationClasses.h +++ b/client/battle/BattleAnimationClasses.h @@ -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 steps; + std::vector effectColors; + std::vector transparency; std::vector timePoints; + const CSpell * spell; float totalProgress; diff --git a/client/battle/BattleEffectsController.cpp b/client/battle/BattleEffectsController.cpp index 0d3caf2ea..41f67e981 100644 --- a/client/battle/BattleEffectsController.cpp +++ b/client/battle/BattleEffectsController.cpp @@ -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; } diff --git a/client/battle/BattleEffectsController.h b/client/battle/BattleEffectsController.h index 255313d21..5aa708dff 100644 --- a/client/battle/BattleEffectsController.h +++ b/client/battle/BattleEffectsController.h @@ -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 effectColors; + std::vector transparency; + std::vector timePoints; +}; + /// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,... struct BattleEffect { diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index d0be307cc..5b8f3c077 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -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(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; diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index 38019befb..c7c3f885a 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -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; diff --git a/client/battle/CreatureAnimation.cpp b/client/battle/CreatureAnimation.cpp index 60a59cf54..c900a46b4 100644 --- a/client/battle/CreatureAnimation.cpp +++ b/client/battle/CreatureAnimation.cpp @@ -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(floor(currentFrame)); std::shared_ptr 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)); } diff --git a/client/battle/CreatureAnimation.h b/client/battle/CreatureAnimation.h index 09e3fd259..168f8d117 100644 --- a/client/battle/CreatureAnimation.h +++ b/client/battle/CreatureAnimation.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); diff --git a/client/render/CanvasImage.h b/client/render/CanvasImage.h index 56452c926..154d317f3 100644 --- a/client/render/CanvasImage.h +++ b/client/render/CanvasImage.h @@ -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{}; diff --git a/client/render/ColorFilter.cpp b/client/render/ColorFilter.cpp index 9e530009f..12ffb7c16 100644 --- a/client/render/ColorFilter.cpp +++ b/client/render/ColorFilter.cpp @@ -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); -} diff --git a/client/render/ColorFilter.h b/client/render/ColorFilter.h index 2a0268fe0..a2403ab52 100644 --- a/client/render/ColorFilter.h +++ b/client/render/ColorFilter.h @@ -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 filters; - std::vector timePoints; }; diff --git a/client/render/IImage.h b/client/render/IImage.h index 9c2216553..cca4a8305 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -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; }; diff --git a/client/renderSDL/ScalableImage.cpp b/client/renderSDL/ScalableImage.cpp index 40a22c722..956679aa8 100644 --- a/client/renderSDL/ScalableImage.cpp +++ b/client/renderSDL/ScalableImage.cpp @@ -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 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 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; + } + } +} diff --git a/client/renderSDL/ScalableImage.h b/client/renderSDL/ScalableImage.h index 7578130c2..82a20c56d 100644 --- a/client/renderSDL/ScalableImage.h +++ b/client/renderSDL/ScalableImage.h @@ -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 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; diff --git a/config/battleEffects.json b/config/battleEffects.json index 498290cd4..6663297c2 100644 --- a/config/battleEffects.json +++ b/config/battleEffects.json @@ -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 ], + "time" : 0.5, + "color" : [ 1.0, 0.0, 0.0, 1.0 ], //red "alpha" : 1.0 }, { "time" : 1.0, + "alpha" : 1.0 }, ], } diff --git a/lib/Color.h b/lib/Color.h index 1bc03414a..a983df691 100644 --- a/lib/Color.h +++ b/lib/Color.h @@ -64,4 +64,18 @@ public: } }; +namespace vstd +{ +template +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