203 lines
5.7 KiB
C++
203 lines
5.7 KiB
C++
// Copyright Steve Streeting
|
|
// Licensed under the MIT License (see License.txt)
|
|
#include "StevesLightFlicker.h"
|
|
|
|
#include "Net/UnrealNetwork.h"
|
|
|
|
TMap<EStevesLightFlickerPattern, FRichCurve> UStevesLightFlickerHelper::Curves;
|
|
TMap<FString, FRichCurve> UStevesLightFlickerHelper::CustomCurves;
|
|
FCriticalSection UStevesLightFlickerHelper::CriticalSection;
|
|
|
|
const TMap<EStevesLightFlickerPattern, FString> UStevesLightFlickerHelper::StandardPatterns {
|
|
// Quake lighting flicker functions
|
|
// https://github.com/id-Software/Quake/blob/bf4ac424ce754894ac8f1dae6a3981954bc9852d/qw-qc/world.qc#L328-L372
|
|
{ EStevesLightFlickerPattern::Flicker1, TEXT("mmnmmommommnonmmonqnmmo") },
|
|
{ EStevesLightFlickerPattern::SlowStrongPulse, TEXT("abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba") },
|
|
{ EStevesLightFlickerPattern::Candle1, TEXT("mmmmmaaaaammmmmaaaaaabcdefgabcdefg") },
|
|
{ EStevesLightFlickerPattern::FastStrobe, TEXT("mamamamamama") },
|
|
{ EStevesLightFlickerPattern::GentlePulse1, TEXT("jklmnopqrstuvwxyzyxwvutsrqponmlkj") },
|
|
{ EStevesLightFlickerPattern::Flicker2, TEXT("nmonqnmomnmomomno") },
|
|
{ EStevesLightFlickerPattern::Candle2, TEXT("mmmaaaabcdefgmmmmaaaammmaamm") },
|
|
{ EStevesLightFlickerPattern::Candle3, TEXT("mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa") },
|
|
{ EStevesLightFlickerPattern::SlowStrobe, TEXT("aaaaaaaazzzzzzzz") },
|
|
{ EStevesLightFlickerPattern::FlourescentFlicker, TEXT("mmamammmmammamamaaamammma") },
|
|
{ EStevesLightFlickerPattern::SlowPulseNoBlack, TEXT("abcdefghijklmnopqrrqponmlkjihgfedcba") },
|
|
|
|
/// Eniko's torch pattern from Kitsune Tails
|
|
{ EStevesLightFlickerPattern::Torch1, TEXT("mnkjcfcdafdehifkjlm") },
|
|
/// No black version of Torch1
|
|
{ EStevesLightFlickerPattern::Torch2, TEXT("mnkjcfcecfdehifkjlm") }
|
|
};
|
|
|
|
float UStevesLightFlickerHelper::EvaluateLightCurve(EStevesLightFlickerPattern CurveType, float Time)
|
|
{
|
|
return GetLightCurve(CurveType).Eval(Time);
|
|
}
|
|
|
|
const FRichCurve& UStevesLightFlickerHelper::GetLightCurve(EStevesLightFlickerPattern CurveType)
|
|
{
|
|
FScopeLock ScopeLock(&CriticalSection);
|
|
|
|
if (auto pCurve = Curves.Find(CurveType))
|
|
{
|
|
return *pCurve;
|
|
}
|
|
|
|
auto& Curve = Curves.Emplace(CurveType);
|
|
BuildCurve(CurveType, Curve);
|
|
return Curve;
|
|
}
|
|
|
|
const FRichCurve& UStevesLightFlickerHelper::GetLightCurve(const FString& CurveStr)
|
|
{
|
|
FScopeLock ScopeLock(&CriticalSection);
|
|
|
|
if (auto pCurve = CustomCurves.Find(CurveStr))
|
|
{
|
|
return *pCurve;
|
|
}
|
|
|
|
auto& Curve = CustomCurves.Emplace(CurveStr);
|
|
BuildCurve(CurveStr, Curve);
|
|
return Curve;
|
|
}
|
|
|
|
void UStevesLightFlickerHelper::BuildCurve(EStevesLightFlickerPattern CurveType, FRichCurve& OutCurve)
|
|
{
|
|
if (auto pTxt = StandardPatterns.Find(CurveType))
|
|
{
|
|
BuildCurve(*pTxt, OutCurve);
|
|
}
|
|
|
|
}
|
|
|
|
void UStevesLightFlickerHelper::BuildCurve(const FString& QuakeCurveChars, FRichCurve& OutCurve)
|
|
{
|
|
OutCurve.Reset();
|
|
|
|
for (int i = 0; i < QuakeCurveChars.Len(); ++i)
|
|
{
|
|
// We actually build the curve a..z = 0..1, and then use a default max value of 2 to restore the original behaviour.
|
|
// Actually the curve is 0..1.04 due to original behaviour that z is 2.08 not 2
|
|
const int CharIndex = QuakeCurveChars[i] - 'a';
|
|
const float Val = (float)CharIndex / 24.f; // to ensure m==1, z==2.08 (rescaled to half that so 0..1.04)
|
|
// Quake default was each character was 0.1s
|
|
OutCurve.AddKey(i * 0.1f, Val);
|
|
}
|
|
|
|
// To catch empty
|
|
if (QuakeCurveChars.IsEmpty())
|
|
{
|
|
OutCurve.AddKey(0, 1);
|
|
}
|
|
}
|
|
|
|
UStevesLightFlickerComponent::UStevesLightFlickerComponent(const FObjectInitializer& Initializer):
|
|
Super(Initializer),
|
|
TimePos(0),
|
|
CurrentValue(0),
|
|
Curve(nullptr)
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
PrimaryComponentTick.bTickEvenWhenPaused = false;
|
|
PrimaryComponentTick.bStartWithTickEnabled = false;
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
GenerateCurveAndPlay();
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::GenerateCurveAndPlay()
|
|
{
|
|
if (FlickerPattern == EStevesLightFlickerPattern::Custom)
|
|
{
|
|
Curve = &UStevesLightFlickerHelper::GetLightCurve(CustomFlickerPattern);
|
|
}
|
|
else
|
|
{
|
|
Curve = &UStevesLightFlickerHelper::GetLightCurve(FlickerPattern);
|
|
}
|
|
TimePos = 0;
|
|
if (bAutoPlay)
|
|
{
|
|
Play();
|
|
}
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::ValueUpdate()
|
|
{
|
|
CurrentValue = FMath::Lerp(MinValue, MaxValue, Curve->Eval(TimePos));
|
|
OnLightFlickerUpdate.Broadcast(CurrentValue);
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::Play(bool bResetTime)
|
|
{
|
|
if (GetOwnerRole() == ROLE_Authority || !GetIsReplicated())
|
|
{
|
|
if (bResetTime)
|
|
{
|
|
TimePos = 0;
|
|
}
|
|
ValueUpdate();
|
|
|
|
PrimaryComponentTick.SetTickFunctionEnable(true);
|
|
}
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::Pause()
|
|
{
|
|
if (GetOwnerRole() == ROLE_Authority || !GetIsReplicated())
|
|
{
|
|
PrimaryComponentTick.SetTickFunctionEnable(false);
|
|
}
|
|
}
|
|
|
|
float UStevesLightFlickerComponent::GetCurrentValue() const
|
|
{
|
|
return CurrentValue;
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::OnRep_TimePos()
|
|
{
|
|
ValueUpdate();
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::TickComponent(float DeltaTime,
|
|
ELevelTick TickType,
|
|
FActorComponentTickFunction* ThisTickFunction)
|
|
{
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
|
|
|
TimePos += DeltaTime * Speed;
|
|
const float MaxTime = Curve->GetLastKey().Time;
|
|
while (TimePos > MaxTime)
|
|
{
|
|
TimePos -= MaxTime;
|
|
}
|
|
ValueUpdate();
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::SetFlickerPattern(EStevesLightFlickerPattern Pattern,
|
|
const FString& CustomPatternString)
|
|
{
|
|
FlickerPattern = Pattern;
|
|
CustomFlickerPattern = CustomPatternString;
|
|
GenerateCurveAndPlay();
|
|
}
|
|
|
|
EStevesLightFlickerPattern UStevesLightFlickerComponent::GetFlickerPattern(FString& CustomString)
|
|
{
|
|
CustomString = CustomFlickerPattern;
|
|
return FlickerPattern;
|
|
}
|
|
|
|
void UStevesLightFlickerComponent::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
|
|
DOREPLIFETIME(UStevesLightFlickerComponent, TimePos);
|
|
}
|