Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs
1 using Microsoft.Xna.Framework;
2 using System;
5 #if CLIENT
6 using Microsoft.Xna.Framework.Graphics;
7 using Barotrauma.Lights;
8 #endif
9 
11 {
13  {
14  private Color lightColor;
18  private float lightBrightness;
19  private float blinkFrequency;
20  private float pulseFrequency, pulseAmount;
21  private float range;
22  private float flicker, flickerSpeed;
23  private bool castShadows;
24  private bool drawBehindSubs;
25 
26  private double lastToggleSignalTime;
27 
28  private string prevColorSignal;
29 
31 
32  private bool isOn;
33 
34  private Turret turret;
35 
36  [Serialize(100.0f, IsPropertySaveable.Yes, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
37  Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
38  public float Range
39  {
40  get { return range; }
41  set
42  {
43  range = MathHelper.Clamp(value, 0.0f, 4096.0f);
44 #if CLIENT
46  if (Light != null) { Light.Range = range; }
47 #endif
48  }
49  }
50 
51  private float rotation;
52  public float Rotation
53  {
54  get { return rotation; }
55  set
56  {
57  rotation = value;
58  SetLightSourceTransformProjSpecific();
59  }
60  }
61 
62  [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should structures cast shadows when light from this light source hits them. " +
63  "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)]
64  public bool CastShadows
65  {
66  get { return castShadows; }
67  set
68  {
69  castShadows = value;
70 #if CLIENT
71  if (Light != null) Light.CastShadows = value;
72 #endif
73  }
74  }
75 
76  [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. " +
77  "It's recommended to enable this on decorative lights outside the submarine's hull.", alwaysUseInstanceValues: true)]
78  public bool DrawBehindSubs
79  {
80  get { return drawBehindSubs; }
81  set
82  {
83  drawBehindSubs = value;
84 #if CLIENT
85  if (Light != null) Light.IsBackground = drawBehindSubs;
86 #endif
87  }
88  }
89 
90  [Editable, Serialize(false, IsPropertySaveable.Yes, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
91  public bool IsOn
92  {
93  get { return isOn; }
94  set
95  {
96  if (isOn == value && IsActive == value) { return; }
97 
98  IsActive = isOn = value;
99  SetLightSourceState(value, value ? lightBrightness : 0.0f);
100  OnStateChanged();
101  }
102  }
103 
104  [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.")]
105  public float Flicker
106  {
107  get { return flicker; }
108  set
109  {
110  flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
111 #if CLIENT
112  if (Light != null) { Light.LightSourceParams.Flicker = flicker; }
113 #endif
114  }
115  }
116 
117  [Editable, Serialize(1.0f, IsPropertySaveable.No, description: "How fast the light flickers.")]
118  public float FlickerSpeed
119  {
120  get { return flickerSpeed; }
121  set
122  {
123  flickerSpeed = value;
124 #if CLIENT
125  if (Light != null) { Light.LightSourceParams.FlickerSpeed = flickerSpeed; }
126 #endif
127  }
128  }
129 
130  [Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
131  public float PulseFrequency
132  {
133  get { return pulseFrequency; }
134  set
135  {
136  pulseFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
137 #if CLIENT
138  if (Light != null) { Light.LightSourceParams.PulseFrequency = pulseFrequency; }
139 #endif
140  }
141  }
142 
143  [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.")]
144  public float PulseAmount
145  {
146  get { return pulseAmount; }
147  set
148  {
149  pulseAmount = MathHelper.Clamp(value, 0.0f, 1.0f);
150 #if CLIENT
151  if (Light != null) { Light.LightSourceParams.PulseAmount = pulseAmount; }
152 #endif
153  }
154  }
155 
156  [Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
157  public float BlinkFrequency
158  {
159  get { return blinkFrequency; }
160  set
161  {
162  blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
163 #if CLIENT
164  if (Light != null) { Light.LightSourceParams.BlinkFrequency = blinkFrequency; }
165 #endif
166  }
167  }
168 
169  [InGameEditable(FallBackTextTag = "connection.setcolor"), Serialize("255,255,255,255", IsPropertySaveable.Yes, description: "The color of the emitted light (R,G,B,A).", alwaysUseInstanceValues: true)]
170  public Color LightColor
171  {
172  get { return lightColor; }
173  set
174  {
175  lightColor = value;
176  //reset previously received signal to force updating the color if we receive a set_color signal after the color has been modified manually
177  prevColorSignal = string.Empty;
178 #if CLIENT
179  if (Light != null)
180  {
181  Light.Color = IsOn ? lightColor.Multiply(lightColorMultiplier) : Color.Transparent;
182  }
183 #endif
184  }
185  }
186 
187  [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).")]
189  {
190  get;
191  set;
192  }
193 
194  [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.")]
195  public bool AlphaBlend
196  {
197  get;
198  set;
199  }
200 
204  public bool IsRed => ColorExtensions.IsRedDominant(LightColor);
205 
209  public bool IsGreen => ColorExtensions.IsGreenDominant(LightColor);
210 
214  public bool IsBlue => ColorExtensions.IsBlueDominant(LightColor);
215 
216 
217  public float TemporaryFlickerTimer;
218 
219  public override void Move(Vector2 amount, bool ignoreContacts = false)
220  {
221 #if CLIENT
222  Light.Position += amount;
223 #endif
224  }
225 
226  public override bool IsActive
227  {
228  get
229  {
230  return base.IsActive;
231  }
232 
233  set
234  {
235  if (base.IsActive == value) { return; }
236  base.IsActive = isOn = value;
237  SetLightSourceState(value, value ? lightBrightness : 0.0f);
238  }
239  }
240 
242  : base(item, element)
243  {
244 #if CLIENT
245  Light = new LightSource(element)
246  {
247  ParentSub = item.CurrentHull?.Submarine,
248  Position = item.Position,
249  CastShadows = castShadows,
250  IsBackground = drawBehindSubs,
251  SpriteScale = Vector2.One * item.Scale * LightSpriteScale,
252  Range = range
253  };
254  Light.LightSourceParams.Flicker = flicker;
256  Light.LightSourceParams.PulseAmount = pulseAmount;
257  Light.LightSourceParams.PulseFrequency = pulseFrequency;
258  Light.LightSourceParams.BlinkFrequency = blinkFrequency;
259 #endif
260 
261  IsActive = IsOn;
262  item.AddTag("light");
263  }
264 
265  public override void OnItemLoaded()
266  {
267  base.OnItemLoaded();
268  SetLightSourceState(IsActive, lightBrightness);
269  turret = item.GetComponent<Turret>();
270  if (item.body != null)
271  {
273  item.body.FarseerBody.OnDisabled += CheckIfNeedsUpdate;
274  }
275 #if CLIENT
276  Drawable = AlphaBlend && Light.LightSprite != null;
278  {
279  OnMapLoaded();
280  }
281 #endif
282  }
283 
284  public override void OnMapLoaded()
285  {
286 #if CLIENT
287  if (item.IsHidden)
288  {
289  Light.Enabled = false;
290  }
291 #endif
293  }
294 
295  public void CheckIfNeedsUpdate()
296  {
297  if (!IsOn)
298  {
299  base.IsActive = false;
300  return;
301  }
302 
303  if ((item.body == null || !item.body.Enabled) &&
304  powerConsumption <= 0.0f && Parent == null && turret == null &&
305  (statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
306  (IsActiveConditionals == null || IsActiveConditionals.Count == 0))
307  {
308  if (item.body == null || item.body.Enabled ||
309  (item.ParentInventory is ItemInventory itemInventory && !itemInventory.Container.HideItems))
310  {
311  lightBrightness = 1.0f;
312  SetLightSourceState(true, lightBrightness);
313  }
314  else
315  {
316  lightBrightness = 0.0f;
317  SetLightSourceState(false, 0.0f);
318  }
319  isOn = true;
320  SetLightSourceTransformProjSpecific();
321  base.IsActive = false;
322 #if CLIENT
324 #endif
325  }
326  else
327  {
328  base.IsActive = true;
329  }
330  }
331 
332  public override void Update(float deltaTime, Camera cam)
333  {
334  if (item.AiTarget != null)
335  {
336  UpdateAITarget(item.AiTarget);
337  }
338  UpdateOnActiveEffects(deltaTime);
339  //something in UpdateOnActiveEffects may deactivate the light -> return so we don't turn it back on
340  if (!IsActive) { return; }
341 
342 #if CLIENT
344 #endif
345 
346 
347  bool visibleInContainer;
348  var ownerCharacter = item.GetRootInventoryOwner() as Character;
349  if (ownerCharacter != null && item.RootContainer?.GetComponent<Holdable>() is not { IsActive: true })
350  {
351  //if the item is in a character inventory, the light should only be visible if the character is holding the item
352  //(not if it's e.q. inside a wearable item, or in a rifle worn on the back)
353  visibleInContainer = false;
354  }
355  else
356  {
357  visibleInContainer = item.FindParentInventory(static it => it is ItemInventory { Container.HideItems: true }) == null;
358  }
359 
360  if ((item.Container != null && !visibleInContainer && ownerCharacter == null) ||
361  (ownerCharacter != null && ownerCharacter.InvisibleTimer > 0.0f))
362  {
363  lightBrightness = 0.0f;
364  SetLightSourceState(false, 0.0f);
365  return;
366  }
367  SetLightSourceTransformProjSpecific();
368 
369  PhysicsBody body = ParentBody ?? item.body;
370  if (body != null && !body.Enabled && !visibleInContainer)
371  {
372  lightBrightness = 0.0f;
373  SetLightSourceState(false, 0.0f);
374  return;
375  }
376 
377  TemporaryFlickerTimer -= deltaTime;
378 
379  //currPowerConsumption = powerConsumption;
380  if (Rand.Range(0.0f, 1.0f) < 0.05f && (Voltage < Rand.Range(0.0f, MinVoltage) || TemporaryFlickerTimer > 0.0f))
381  {
382 #if CLIENT
383  if (Voltage > 0.1f)
384  {
385  SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
386  }
387 #endif
388  lightBrightness = 0.0f;
389  }
390  else
391  {
392  lightBrightness = MathHelper.Lerp(lightBrightness, powerConsumption <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f), 0.1f);
393  }
394 
395  SetLightSourceState(true, lightBrightness);
396  }
397 
398  public override void UpdateBroken(float deltaTime, Camera cam)
399  {
400  SetLightSourceState(false, 0.0f);
401  }
402 
403  public override bool Use(float deltaTime, Character character = null)
404  {
405  return true;
406  }
407 
408  partial void OnStateChanged();
409 
410  public override void ReceiveSignal(Signal signal, Connection connection)
411  {
412  switch (connection.Name)
413  {
414  case "toggle":
415  if (signal.value != "0")
416  {
417  if (!IgnoreContinuousToggle || lastToggleSignalTime < Timing.TotalTime - 0.1)
418  {
419  IsOn = !IsOn;
420  }
421  lastToggleSignalTime = Timing.TotalTime;
422  }
423  break;
424  case "set_state":
425  IsOn = signal.value != "0";
426  break;
427  case "set_color":
428  if (signal.value != prevColorSignal)
429  {
430  LightColor = XMLExtensions.ParseColor(signal.value, false);
431 #if CLIENT
432  SetLightSourceState(Light.Enabled, lightColorMultiplier);
433 #endif
434  prevColorSignal = signal.value;
435  }
436  break;
437  }
438  }
439 
440  private void UpdateAITarget(AITarget target)
441  {
442  if (!IsActive) { return; }
443  if (target.MaxSightRange <= 0)
444  {
445  target.MaxSightRange = Range * 5;
446  }
447  target.SightRange = Math.Max(target.SightRange, target.MaxSightRange * lightBrightness);
448  }
449 
450  public override void Drop(Character dropper, bool setTransform = true)
451  {
453  }
454 
455  partial void SetLightSourceState(bool enabled, float brightness);
456 
458  {
459  SetLightSourceTransformProjSpecific();
460  }
461 
462  partial void SetLightSourceTransformProjSpecific();
463  }
464 }
AITarget AiTarget
Definition: Entity.cs:55
Submarine Submarine
Definition: Entity.cs:53
Inventory FindParentInventory(Func< Inventory, bool > predicate)
readonly Dictionary< ActionType, List< StatusEffect > > statusEffectLists
bool IsRed
Returns true if the red component of the light is twice as bright as the blue and green....
bool IsGreen
Returns true if the green component of the light is twice as bright as the red and blue....
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
bool IsBlue
Returns true if the blue component of the light is twice as bright as the red and green....
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
float powerConsumption
The maximum amount of power the item can draw from connected items
LightSourceParams LightSourceParams
Definition: LightSource.cs:291
bool IsBackground
Background lights are drawn behind submarines and they don't cast shadows.
Definition: LightSource.cs:442
bool IsHidden
Is the entity hidden due to HiddenInGame being enabled or the layer the entity is in being hidden?
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19