Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs
T
Joonas Rikkonen 5dc31c213f ad0bbaf...7245c72
commit 7245c721339885d062567befc052a592391b3b4a
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 10 15:22:31 2019 +0200

    Fixed StatusEffects only applying afflictions to one limb even if the target is "Character" instead of "Limb", added a subtle screen distortion effect to heavy radiation sickness. Closes #1256

commit e0db27e62ec9546fd4b182a0cc97f7e5830645ae
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 21:53:51 2019 +0200

    Fixed WrapText adding unnecessary spaces after every line break. Closes #1215

commit 988bc58d51c195ad9265b84a1e97e0101cd3f808
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 21:12:50 2019 +0200

    Fixed crashing when attempting to create a body for a wall section that's less than 1 unit long (e.g. if a wall that's just slightly longer than the wall section size receives damage).

commit 8c31157425a9e2ec02312618d1bfa359ab3ee87d
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 20:30:44 2019 +0200

    Fixed clients being unable to toggle the respawn shuttle on/off

commit a4ccb039219830efe9cd305c26942dda1bd04e9c
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 19:33:22 2019 +0200

    Fixed inability to select the respawn shuttle as a client host

commit b89b2d2c282d8c74d7ccd37b3f29dcab51eff680
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 19:32:41 2019 +0200

    Made it possible to edit the style of the ListBox under GUIDropDowns, increased the opacity of the listbox to make the contents more readable when there's text behind it

commit 8f6d9aef3d637fe37a18c78f4b15ef8fd266374e
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sat Mar 9 18:11:23 2019 +0200

    Fixed NetLobbyScreen not showing the names of the submarines the client doesn't have
2019-03-18 22:39:57 +02:00

555 lines
20 KiB
C#

using Barotrauma.Networking;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
enum SoundSelectionMode
{
Random,
CharacterSpecific,
ItemSpecific,
All
}
class ItemSound
{
public readonly RoundSound RoundSound;
public readonly ActionType Type;
public string VolumeProperty;
public float VolumeMultiplier
{
get { return RoundSound.Volume; }
}
public float Range
{
get { return RoundSound.Range; }
}
public float VolumeMultiplier
{
get { return RoundSound.Volume; }
}
public float Range
{
get { return RoundSound.Range; }
}
public readonly bool Loop;
public ItemSound(RoundSound sound, ActionType type, bool loop = false)
{
this.RoundSound = sound;
this.Type = type;
this.Loop = loop;
}
}
partial class ItemComponent : ISerializableEntity
{
private Dictionary<ActionType, List<ItemSound>> sounds;
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
public GUILayoutSettings DefaultLayout { get; protected set; }
public GUILayoutSettings AlternativeLayout { get; protected set; }
public class GUILayoutSettings
{
public Vector2? RelativeSize { get; private set; }
public Point? AbsoluteSize { get; private set; }
public Vector2? RelativeOffset { get; private set; }
public Point? AbsoluteOffset { get; private set; }
public Anchor? Anchor { get; private set; }
public Pivot? Pivot { get; private set; }
public static GUILayoutSettings Load(XElement element)
{
var layout = new GUILayoutSettings();
var relativeSize = XMLExtensions.GetAttributeVector2(element, "relativesize", Vector2.Zero);
var absoluteSize = XMLExtensions.GetAttributePoint(element, "absolutesize", new Point(-1000, -1000));
var relativeOffset = XMLExtensions.GetAttributeVector2(element, "relativeoffset", Vector2.Zero);
var absoluteOffset = XMLExtensions.GetAttributePoint(element, "absoluteoffset", new Point(-1000, -1000));
if (relativeSize.Length() > 0)
{
layout.RelativeSize = relativeSize;
}
if (absoluteSize.X > 0 && absoluteSize.Y > 0)
{
layout.AbsoluteSize = absoluteSize;
}
if (relativeOffset.Length() > 0)
{
layout.RelativeOffset = relativeOffset;
}
if (absoluteOffset.X > -1000 && absoluteOffset.Y > -1000)
{
layout.AbsoluteOffset = absoluteOffset;
}
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "anchor", ""), out Anchor a))
{
layout.Anchor = a;
}
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "pivot", ""), out Pivot p))
{
layout.Pivot = p;
}
return layout;
}
public void ApplyTo(RectTransform target)
{
if (RelativeOffset.HasValue)
{
target.RelativeOffset = RelativeOffset.Value;
}
else if (AbsoluteOffset.HasValue)
{
target.AbsoluteOffset = AbsoluteOffset.Value;
}
if (RelativeSize.HasValue)
{
target.RelativeSize = RelativeSize.Value;
}
else if (AbsoluteSize.HasValue)
{
target.NonScaledSize = AbsoluteSize.Value;
}
if (Anchor.HasValue)
{
target.Anchor = Anchor.Value;
}
if (Pivot.HasValue)
{
target.Pivot = Pivot.Value;
}
else
{
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
}
target.RecalculateChildren(true, true);
}
}
public GUIFrame GuiFrame { get; protected set; }
[Serialize(false, false)]
public bool AllowUIOverlap
{
get;
set;
}
private ItemComponent linkToUIComponent;
[Serialize("", false)]
public string LinkUIToComponent
{
get;
set;
}
[Serialize(0, false)]
public int HudPriority
{
get;
private set;
}
private bool useAlternativeLayout;
public bool UseAlternativeLayout
{
get { return useAlternativeLayout; }
set
{
if (AlternativeLayout != null)
{
if (value == useAlternativeLayout) { return; }
useAlternativeLayout = value;
if (useAlternativeLayout)
{
AlternativeLayout?.ApplyTo(GuiFrame.RectTransform);
}
else
{
DefaultLayout?.ApplyTo(GuiFrame.RectTransform);
}
}
}
public void ApplyTo(RectTransform target)
{
if (RelativeOffset.HasValue)
{
target.RelativeOffset = RelativeOffset.Value;
}
else if (AbsoluteOffset.HasValue)
{
target.AbsoluteOffset = AbsoluteOffset.Value;
}
if (RelativeSize.HasValue)
{
target.RelativeSize = RelativeSize.Value;
}
else if (AbsoluteSize.HasValue)
{
target.NonScaledSize = AbsoluteSize.Value;
}
if (Anchor.HasValue)
{
target.Anchor = Anchor.Value;
}
if (Pivot.HasValue)
{
target.Pivot = Pivot.Value;
}
else
{
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
}
target.RecalculateChildren(true, true);
}
}
public GUIFrame GuiFrame { get; protected set; }
[Serialize(false, false)]
public bool AllowUIOverlap
{
get;
set;
}
private ItemComponent linkToUIComponent;
[Serialize("", false)]
public string LinkUIToComponent
{
get;
set;
}
[Serialize(0, false)]
public int HudPriority
{
get;
private set;
}
private bool shouldMuffleLooping;
private float lastMuffleCheckTime;
private ItemSound loopingSound;
private SoundChannel loopingSoundChannel;
public void PlaySound(ActionType type, Vector2 position, Character user = null)
{
if (loopingSound != null)
{
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > loopingSound.Range * loopingSound.Range)
{
if (loopingSoundChannel != null)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
return;
}
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
GetSoundVolume(loopingSound),
SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
}
if (loopingSoundChannel != null)
{
if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
{
shouldMuffleLooping = SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull);
lastMuffleCheckTime = (float)Timing.TotalTime;
}
loopingSoundChannel.Muffled = shouldMuffleLooping;
loopingSoundChannel.Gain = GetSoundVolume(loopingSound);
loopingSoundChannel.Position = new Vector3(position.X, position.Y, 0.0f);
}
return;
}
if (!sounds.TryGetValue(type, out List<ItemSound> matchingSounds)) return;
ItemSound itemSound = null;
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
SoundSelectionMode soundSelectionMode = soundSelectionModes[type];
int index;
if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && user != null)
{
index = user.ID % matchingSounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.ItemSpecific)
{
index = item.ID % matchingSounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (ItemSound sound in matchingSounds)
{
PlaySound(sound, position, user);
}
return;
}
else
{
index = Rand.Int(matchingSounds.Count);
}
itemSound = matchingSounds[index];
PlaySound(matchingSounds[index], position, user);
}
}
private void PlaySound(ItemSound itemSound, Vector2 position, Character user = null)
{
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > itemSound.Range * itemSound.Range)
{
return;
}
if (itemSound.Loop)
{
loopingSound = itemSound;
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
GetSoundVolume(loopingSound),
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
}
}
else
{
float volume = GetSoundVolume(itemSound);
if (volume <= 0.0f) return;
SoundPlayer.PlaySound(itemSound.RoundSound.Sound, volume, itemSound.Range, position, item.CurrentHull);
}
}
public void StopSounds(ActionType type)
{
if (loopingSound == null) return;
if (loopingSound.Type != type) return;
if (loopingSoundChannel != null)
{
loopingSoundChannel.Dispose();
loopingSoundChannel = null;
loopingSound = null;
}
}
private float GetSoundVolume(ItemSound sound)
{
if (sound == null) { return 0.0f; }
if (sound.VolumeProperty == "") { return sound.VolumeMultiplier; }
if (properties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
{
float newVolume = 0.0f;
try
{
newVolume = (float)property.GetValue(this);
}
catch
{
return 0.0f;
}
newVolume *= sound.VolumeMultiplier;
if (!MathUtils.IsValid(newVolume))
{
DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
GameAnalyticsManager.AddErrorEventOnce(
"ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
return 0.0f;
}
return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
}
return 0.0f;
}
public virtual bool ShouldDrawHUD(Character character)
{
return true;
}
public ItemComponent GetLinkUIToComponent()
{
if (string.IsNullOrEmpty(LinkUIToComponent))
{
return null;
}
foreach (ItemComponent component in item.Components)
{
if (component.name.ToLower() == LinkUIToComponent.ToLower())
{
linkToUIComponent = component;
}
}
if (linkToUIComponent == null)
{
DebugConsole.ThrowError("Failed to link the component \"" + Name + "\" to \"" + LinkUIToComponent + "\" in the item \"" + item.Name + "\" - component with a matching name not found.");
}
return linkToUIComponent;
}
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
public virtual void AddToGUIUpdateList()
{
GuiFrame?.AddToGUIUpdateList();
}
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
private bool LoadElemProjSpecific(XElement subElement)
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "guiframe":
if (subElement.Attribute("rect") != null)
{
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - GUIFrame defined as rect, use RectTransform instead.");
break;
}
Color? color = null;
if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White);
string style = subElement.Attribute("style") == null ?
null : subElement.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas), style, color);
DefaultLayout = GUILayoutSettings.Load(subElement);
break;
case "alternativelayout":
AlternativeLayout = GUILayoutSettings.Load(subElement);
break;
case "itemsound":
case "sound":
string filePath = subElement.GetAttributeString("file", "");
if (filePath == "") filePath = subElement.GetAttributeString("sound", "");
if (filePath == "")
{
DebugConsole.ThrowError("Error when instantiating item \"" + item.Name + "\" - sound with no file path set");
break;
}
if (!filePath.Contains("/") && !filePath.Contains("\\") && !filePath.Contains(Path.DirectorySeparatorChar))
{
filePath = Path.Combine(Path.GetDirectoryName(item.Prefab.ConfigFile), filePath);
}
ActionType type;
try
{
type = (ActionType)Enum.Parse(typeof(ActionType), subElement.GetAttributeString("type", ""), true);
}
catch (Exception e)
{
DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e);
break;
}
RoundSound sound = Submarine.LoadRoundSound(subElement);
if (sound == null) { break; }
ItemSound itemSound = new ItemSound(sound, type, subElement.GetAttributeBool("loop", false))
{
VolumeProperty = subElement.GetAttributeString("volumeproperty", "").ToLowerInvariant()
};
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
{
SoundSelectionMode selectionMode = SoundSelectionMode.Random;
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode);
soundSelectionModes[type] = selectionMode;
}
List<ItemSound> soundList = null;
if (!sounds.TryGetValue(itemSound.Type, out soundList))
{
soundList = new List<ItemSound>();
sounds.Add(itemSound.Type, soundList);
}
soundList.Add(itemSound);
break;
default:
return false; //unknown element
}
return true; //element processed
}
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
protected void StartDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync = false)
{
if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
}
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, NetBuffer buffer, float sendingTime, bool waitForMidRoundSync)
{
while (GameMain.Client != null &&
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))
{
correctionTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
if (item.Removed || GameMain.Client == null)
{
yield return CoroutineStatus.Success;
}
((IServerSerializable)this).ClientRead(type, buffer, sendingTime);
correctionTimer = 0.0f;
delayedCorrectionCoroutine = null;
yield return CoroutineStatus.Success;
}
}
}