1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-17 20:58:07 +02:00

Implemented bloodlust, clone, and petrify effects for xbrz mode

This commit is contained in:
Ivan Savenko 2025-02-08 14:47:01 +00:00
parent de3b7e3cc6
commit 28a8d4f4a1
16 changed files with 151 additions and 125 deletions

View File

@ -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());
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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
{

View File

@ -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;

View File

@ -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;

View File

@ -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));
}

View File

@ -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);

View File

@ -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{};

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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
},
],
}

View File

@ -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