using Barotrauma.Extensions;
using Barotrauma.Lights;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma.Items.Components
{
partial class LightComponent : Powered, IServerSerializable, IDrawableComponent
{
private bool? lastReceivedState;
private CoroutineHandle resetPredictionCoroutine;
private float resetPredictionTimer;
///
/// The current multiplier for the light color (usually equal to , but in the case of e.g. blinking lights the multiplier
/// doesn't go to 0 when the light turns off, because otherwise it'd take a while for it turn back on based on the lightBrightness which is interpolated
/// towards the current voltage).
///
private float lightColorMultiplier;
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The scale of the light sprite.")]
public float LightSpriteScale { get; set; }
public Vector2 DrawSize
{
get { return new Vector2(Light.Range * 2, Light.Range * 2); }
}
public LightSource Light { get; }
public override void OnScaleChanged()
{
Light.SpriteScale = Vector2.One * item.Scale;
Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
SetLightSourceTransformProjSpecific();
}
partial void SetLightSourceState(bool enabled, float brightness)
{
if (Light == null) { return; }
if (item.IsHidden) { enabled = false; }
Light.Enabled = enabled;
lightColorMultiplier = brightness;
if (enabled)
{
Light.Color = LightColor.Multiply(lightColorMultiplier);
}
}
partial void SetLightSourceTransformProjSpecific()
{
Vector2 offset = LightOffset * item.Scale;
if (offset != Vector2.Zero)
{
if (item.FlippedX) { offset.X *= -1; }
if (item.FlippedY) { offset.Y *= -1; }
offset = Vector2.Transform(offset, Matrix.CreateRotationZ(-item.RotationRad));
}
if (ParentBody != null)
{
Light.ParentBody = ParentBody;
Light.OffsetFromBody = offset;
}
else if (turret != null)
{
Light.Position = new Vector2(item.Rect.X + turret.TransformedBarrelPos.X, item.Rect.Y - turret.TransformedBarrelPos.Y) + offset;
}
else if (item.body != null)
{
Light.ParentBody = item.body;
Light.OffsetFromBody = offset;
}
else
{
Light.Position = item.Position + offset;
}
PhysicsBody body = Light.ParentBody;
if (body != null)
{
Light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
if (body.Enabled)
{
Light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
else
{
Light.LightSpriteEffect = item.SpriteEffects;
}
}
else
{
Light.Rotation = -Rotation - item.RotationRad;
Light.LightSpriteEffect = item.SpriteEffects;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
{
if (Light?.LightSprite == null) { return; }
if ((item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
{
Vector2 offset = LightOffset * item.Scale;
if (item.FlippedX) { offset.X *= -1; }
if (item.FlippedY) { offset.Y *= -1; }
offset = Vector2.Transform(offset, Matrix.CreateRotationZ(-item.RotationRad));
Vector2 origin = Light.LightSprite.Origin;
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
if ((Light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = Light.LightSprite.SourceRect.Height - origin.Y; }
Vector2 drawPos = item.body?.DrawPosition ?? item.DrawPosition + offset;
Color color = lightColor;
if (Light.OverrideLightSpriteAlpha.HasValue)
{
color = new Color(lightColor, Light.OverrideLightSpriteAlpha.Value);
}
Light.LightSprite.Draw(spriteBatch,
new Vector2(drawPos.X, -drawPos.Y),
color * lightBrightness,
origin,
-Light.Rotation,
item.Scale * LightSpriteScale,
Light.LightSpriteEffect, itemDepth - 0.0001f);
}
}
public override void FlipX(bool relativeToSub)
{
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX)
{
Light.LightSpriteEffect ^= SpriteEffects.FlipHorizontally;
}
SetLightSourceTransformProjSpecific();
}
public override void FlipY(bool relativeToSub)
{
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipY)
{
Light.LightSpriteEffect ^= SpriteEffects.FlipVertically;
}
SetLightSourceTransformProjSpecific();
}
partial void OnStateChanged()
{
if (GameMain.Client == null || !lastReceivedState.HasValue) { return; }
//reset to last known server state after the state hasn't changed in 1.0 seconds client-side
resetPredictionTimer = 1.0f;
if (resetPredictionCoroutine == null || !CoroutineManager.IsCoroutineRunning(resetPredictionCoroutine))
{
resetPredictionCoroutine = CoroutineManager.StartCoroutine(ResetPredictionAfterDelay());
}
}
///
/// Reset client-side prediction of the light's state to the last known state sent by the server after resetPredictionTimer runs out
///
private IEnumerable ResetPredictionAfterDelay()
{
while (resetPredictionTimer > 0.0f)
{
resetPredictionTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
if (lastReceivedState.HasValue) { IsActive = lastReceivedState.Value; }
resetPredictionCoroutine = null;
yield return CoroutineStatus.Success;
}
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
IsActive = msg.ReadBoolean();
lastReceivedState = IsActive;
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
Light.Remove();
}
}
}