Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs
2023-03-13 10:30:37 +02:00

412 lines
13 KiB
C#

using Microsoft.Xna.Framework;
using System;
using Barotrauma.Networking;
using Barotrauma.Extensions;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Lights;
#endif
namespace Barotrauma.Items.Components
{
partial class LightComponent : Powered, IServerSerializable, IDrawableComponent
{
private Color lightColor;
/// <summary>
/// The current brightness of the light source, affected by powerconsumption/voltage
/// </summary>
private float lightBrightness;
private float blinkFrequency;
private float pulseFrequency, pulseAmount;
private float range;
private float flicker, flickerSpeed;
private bool castShadows;
private bool drawBehindSubs;
private double lastToggleSignalTime;
private string prevColorSignal;
public PhysicsBody ParentBody;
private bool isOn;
private Turret turret;
[Serialize(100.0f, IsPropertySaveable.Yes, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
public float Range
{
get { return range; }
set
{
range = MathHelper.Clamp(value, 0.0f, 4096.0f);
#if CLIENT
item.ResetCachedVisibleSize();
if (Light != null) { Light.Range = range; }
#endif
}
}
private float rotation;
public float Rotation
{
get { return rotation; }
set
{
rotation = value;
SetLightSourceTransformProjSpecific();
}
}
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should structures cast shadows when light from this light source hits them. " +
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range. Lights that are set to be drawn behind subs don't cast shadows, regardless of this setting.", alwaysUseInstanceValues: true)]
public bool CastShadows
{
get { return castShadows; }
set
{
castShadows = value;
#if CLIENT
if (Light != null) Light.CastShadows = value;
#endif
}
}
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Lights drawn behind submarines don't cast any shadows and are much faster to draw than shadow-casting lights. " +
"It's recommended to enable this on decorative lights outside the submarine's hull.", alwaysUseInstanceValues: true)]
public bool DrawBehindSubs
{
get { return drawBehindSubs; }
set
{
drawBehindSubs = value;
#if CLIENT
if (Light != null) Light.IsBackground = drawBehindSubs;
#endif
}
}
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
public bool IsOn
{
get { return isOn; }
set
{
if (isOn == value && IsActive == value) { return; }
IsActive = isOn = value;
SetLightSourceState(value, value ? lightBrightness : 0.0f);
OnStateChanged();
}
}
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
public float Flicker
{
get { return flicker; }
set
{
flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
if (Light != null) { Light.LightSourceParams.Flicker = flicker; }
#endif
}
}
[Editable, Serialize(1.0f, IsPropertySaveable.No, description: "How fast the light flickers.")]
public float FlickerSpeed
{
get { return flickerSpeed; }
set
{
flickerSpeed = value;
#if CLIENT
if (Light != null) { Light.LightSourceParams.FlickerSpeed = flickerSpeed; }
#endif
}
}
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
public float PulseFrequency
{
get { return pulseFrequency; }
set
{
pulseFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
#if CLIENT
if (Light != null) { Light.LightSourceParams.PulseFrequency = pulseFrequency; }
#endif
}
}
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
public float PulseAmount
{
get { return pulseAmount; }
set
{
pulseAmount = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
if (Light != null) { Light.LightSourceParams.PulseAmount = pulseAmount; }
#endif
}
}
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
public float BlinkFrequency
{
get { return blinkFrequency; }
set
{
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
#if CLIENT
if (Light != null) { Light.LightSourceParams.BlinkFrequency = blinkFrequency; }
#endif
}
}
[InGameEditable(FallBackTextTag = "connection.setcolor"), Serialize("255,255,255,255", IsPropertySaveable.Yes, description: "The color of the emitted light (R,G,B,A).", alwaysUseInstanceValues: true)]
public Color LightColor
{
get { return lightColor; }
set
{
lightColor = value;
#if CLIENT
if (Light != null)
{
Light.Color = IsOn ? lightColor.Multiply(lightColorMultiplier) : Color.Transparent;
}
#endif
}
}
[Serialize(false, IsPropertySaveable.No, description: "If enabled, the component will ignore continuous signals received in the toggle input (i.e. a continuous signal will only toggle it once).")]
public bool IgnoreContinuousToggle
{
get;
set;
}
[Serialize(true, IsPropertySaveable.No, description: "Should the light sprite be drawn on the item using alpha blending, in addition to being rendered in the light map? Can be used to make the light sprite stand out more.")]
public bool AlphaBlend
{
get;
set;
}
public float TemporaryFlickerTimer;
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
#if CLIENT
Light.Position += amount;
#endif
}
public override bool IsActive
{
get
{
return base.IsActive;
}
set
{
if (base.IsActive == value) { return; }
base.IsActive = isOn = value;
SetLightSourceState(value, value ? lightBrightness : 0.0f);
}
}
public LightComponent(Item item, ContentXElement element)
: base(item, element)
{
#if CLIENT
Light = new LightSource(element)
{
ParentSub = item.CurrentHull?.Submarine,
Position = item.Position,
CastShadows = castShadows,
IsBackground = drawBehindSubs,
SpriteScale = Vector2.One * item.Scale,
Range = range
};
Light.LightSourceParams.Flicker = flicker;
Light.LightSourceParams.FlickerSpeed = FlickerSpeed;
Light.LightSourceParams.PulseAmount = pulseAmount;
Light.LightSourceParams.PulseFrequency = pulseFrequency;
Light.LightSourceParams.BlinkFrequency = blinkFrequency;
#endif
IsActive = IsOn;
item.AddTag("light");
}
public override void OnItemLoaded()
{
base.OnItemLoaded();
SetLightSourceState(IsActive, lightBrightness);
turret = item.GetComponent<Turret>();
#if CLIENT
Drawable = AlphaBlend && Light.LightSprite != null;
if (Screen.Selected.IsEditor)
{
OnMapLoaded();
}
#endif
}
public override void OnMapLoaded()
{
#if CLIENT
if (item.HiddenInGame)
{
Light.Enabled = false;
}
#endif
CheckIfNeedsUpdate();
}
public void CheckIfNeedsUpdate()
{
if (!IsOn)
{
base.IsActive = false;
return;
}
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
SetLightSourceTransformProjSpecific();
base.IsActive = false;
isOn = true;
#if CLIENT
Light.ParentSub = item.Submarine;
#endif
}
else
{
base.IsActive = true;
}
}
public override void Update(float deltaTime, Camera cam)
{
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
}
UpdateOnActiveEffects(deltaTime);
//something in UpdateOnActiveEffects may deactivate the light -> return so we don't turn it back on
if (!IsActive) { return; }
#if CLIENT
Light.ParentSub = item.Submarine;
#endif
if (item.Container != null && item.GetRootInventoryOwner() is not Character)
{
lightBrightness = 0.0f;
SetLightSourceState(false, 0.0f);
return;
}
SetLightSourceTransformProjSpecific();
PhysicsBody body = ParentBody ?? item.body;
if (body != null && !body.Enabled)
{
lightBrightness = 0.0f;
SetLightSourceState(false, 0.0f);
return;
}
TemporaryFlickerTimer -= deltaTime;
//currPowerConsumption = powerConsumption;
if (Rand.Range(0.0f, 1.0f) < 0.05f && (Voltage < Rand.Range(0.0f, MinVoltage) || TemporaryFlickerTimer > 0.0f))
{
#if CLIENT
if (Voltage > 0.1f)
{
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
}
#endif
lightBrightness = 0.0f;
}
else
{
lightBrightness = MathHelper.Lerp(lightBrightness, powerConsumption <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f), 0.1f);
}
SetLightSourceState(true, lightBrightness);
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
SetLightSourceState(false, 0.0f);
}
public override bool Use(float deltaTime, Character character = null)
{
return true;
}
partial void OnStateChanged();
public override void ReceiveSignal(Signal signal, Connection connection)
{
switch (connection.Name)
{
case "toggle":
if (signal.value != "0")
{
if (!IgnoreContinuousToggle || lastToggleSignalTime < Timing.TotalTime - 0.1)
{
IsOn = !IsOn;
}
lastToggleSignalTime = Timing.TotalTime;
}
break;
case "set_state":
IsOn = signal.value != "0";
break;
case "set_color":
if (signal.value != prevColorSignal)
{
LightColor = XMLExtensions.ParseColor(signal.value, false);
#if CLIENT
SetLightSourceState(Light.Enabled, lightColorMultiplier);
#endif
prevColorSignal = signal.value;
}
break;
}
}
private void UpdateAITarget(AITarget target)
{
if (!IsActive) { return; }
if (target.MaxSightRange <= 0)
{
target.MaxSightRange = Range * 5;
}
target.SightRange = Math.Max(target.SightRange, target.MaxSightRange * lightBrightness);
}
partial void SetLightSourceState(bool enabled, float brightness);
public void SetLightSourceTransform()
{
SetLightSourceTransformProjSpecific();
}
partial void SetLightSourceTransformProjSpecific();
}
}