3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Graphics;
6 using System.Collections.Generic;
45 this.RoundSound = sound;
48 this.OnlyPlayInSameSub = onlyPlayInSameSub;
56 get {
return sounds.Count > 0; }
61 private readonly
bool[] hasSoundsOfType;
62 private readonly Dictionary<ActionType, List<ItemSound>> sounds;
63 private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
86 var relativeSize = XMLExtensions.GetAttributeVector2(element,
"relativesize", Vector2.Zero);
87 var absoluteSize = XMLExtensions.GetAttributePoint(element,
"absolutesize",
new Point(-1000, -1000));
88 var relativeOffset = XMLExtensions.GetAttributeVector2(element,
"relativeoffset", Vector2.Zero);
89 var absoluteOffset = XMLExtensions.GetAttributePoint(element,
"absoluteoffset",
new Point(-1000, -1000));
90 if (relativeSize.Length() > 0)
92 layout.RelativeSize = relativeSize;
94 if (absoluteSize.X > 0 && absoluteSize.Y > 0)
96 layout.AbsoluteSize = absoluteSize;
98 if (relativeOffset.Length() > 0)
100 layout.RelativeOffset = relativeOffset;
102 if (absoluteOffset.X > -1000 && absoluteOffset.Y > -1000)
104 layout.AbsoluteOffset = absoluteOffset;
106 if (Enum.TryParse(XMLExtensions.GetAttributeString(element,
"anchor",
""), out
Anchor a))
110 if (Enum.TryParse(XMLExtensions.GetAttributeString(element,
"pivot",
""), out
Pivot p))
155 private bool guiFrameUpdatePending;
186 private bool useAlternativeLayout;
189 get {
return useAlternativeLayout; }
194 if (value == useAlternativeLayout) {
return; }
195 useAlternativeLayout = value;
196 if (useAlternativeLayout)
208 private bool shouldMuffleLooping;
209 private float lastMuffleCheckTime;
212 private readonly List<SoundChannel> playingOneshotSoundChannels =
new List<SoundChannel>();
222 if (hasSoundsOfType[(
int)
ActionType.Always]) {
return true; }
223 if (loopingSoundChannel !=
null && loopingSoundChannel.
IsPlaying) {
return true; }
224 if (playingOneshotSoundChannels.Count > 0) {
return true; }
230 if (loopingSound !=
null && loopingSoundChannel !=
null && loopingSoundChannel.
IsPlaying)
232 if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
235 lastMuffleCheckTime = (float)Timing.TotalTime;
237 loopingSoundChannel.
Muffled = shouldMuffleLooping;
238 float targetGain = GetSoundVolume(loopingSound);
239 float gainDiff = targetGain - loopingSoundChannel.
Gain;
240 loopingSoundChannel.
Gain += Math.Abs(gainDiff) < 0.1f ? gainDiff : Math.Sign(gainDiff) * 0.1f;
243 for (
int i = 0; i < playingOneshotSoundChannels.Count; i++)
245 if (!playingOneshotSoundChannels[i].IsPlaying)
247 playingOneshotSoundChannels[i].Dispose();
248 playingOneshotSoundChannels[i] =
null;
251 playingOneshotSoundChannels.RemoveAll(ch => ch ==
null);
252 foreach (
SoundChannel channel
in playingOneshotSoundChannels)
260 if (!hasSoundsOfType[(
int)type]) {
return; }
269 if (loopingSound !=
null)
272 (GetSoundVolume(loopingSound)) <= 0.0001f)
274 if (loopingSoundChannel !=
null)
277 loopingSoundChannel =
null;
286 loopingSoundChannel =
null;
290 if (loopingSoundChannel ==
null || !loopingSoundChannel.
IsPlaying)
297 loopingSoundChannel.
Looping =
true;
300 loopingSoundChannel.
Near = loopingSound.
Range * 0.4f;
301 loopingSoundChannel.
Far = loopingSound.
Range;
308 var playingIndex = sounds[type].IndexOf(loopingSound);
310 if (playingIndex != shouldBePlayingIndex)
313 loopingSoundChannel =
null;
320 var matchingSounds = sounds[type];
321 if (loopingSoundChannel ==
null || !loopingSoundChannel.
IsPlaying)
327 index = user.ID % matchingSounds.Count;
331 index =
item.
ID % matchingSounds.Count;
335 foreach (
ItemSound sound
in matchingSounds)
347 index = Rand.Int(matchingSounds.Count);
372 if (loopingSoundChannel ==
null || !loopingSoundChannel.
IsPlaying)
374 float volume = GetSoundVolume(itemSound);
375 if (volume <= 0.0001f) {
return; }
376 loopingSound = itemSound;
378 new Vector3(position.X, position.Y, 0.0f),
381 muffle: SoundPlayer.ShouldMuffleSound(
Character.Controlled, position, loopingSound.
Range,
Character.Controlled?.CurrentHull));
382 loopingSoundChannel.
Looping =
true;
384 loopingSoundChannel.
Near = loopingSound.
Range * 0.4f;
385 loopingSoundChannel.
Far = loopingSound.
Range;
390 float volume = GetSoundVolume(itemSound);
391 if (volume <= 0.0001f) {
return; }
393 if (channel !=
null) { playingOneshotSoundChannels.Add(channel); }
399 if (loopingSound ==
null) {
return; }
400 if (loopingSoundChannel !=
null)
403 loopingSoundChannel =
null;
410 if (loopingSound ==
null || loopingSound.
Type != type) {
return; }
414 private float GetSoundVolume(
ItemSound sound)
416 if (sound ==
null) {
return 0.0f; }
424 newVolume =
property.GetFloatValue(
this);
432 if (!MathUtils.IsValid(newVolume))
434 DebugConsole.Log(
"Invalid sound volume (item " +
item.
Name +
", " + GetType().ToString() +
"): " + newVolume);
435 GameAnalyticsManager.AddErrorEventOnce(
436 "ItemComponent.PlaySound:" +
item.
Name + GetType().ToString(),
437 GameAnalyticsManager.ErrorSeverity.Error,
438 "Invalid sound volume (item " +
item.
Name +
", " + GetType().ToString() +
"): " + newVolume);
442 return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
461 if (component.name.Equals(
LinkUIToComponent, StringComparison.OrdinalIgnoreCase))
463 linkToUIComponent = component;
466 if (linkToUIComponent ==
null)
468 DebugConsole.ThrowError(
"Failed to link the component \"" +
Name +
"\" to \"" +
LinkUIToComponent +
"\" in the item \"" +
item.
Name +
"\" - component with a matching name not found.");
470 return linkToUIComponent;
486 guiFrameUpdatePending =
false;
504 switch (subElement.
Name.ToString().ToLowerInvariant())
509 DebugConsole.ThrowError($
"Error in item config \"{item.ConfigFilePath}\" - GUIFrame defined as rect, use RectTransform instead.",
513 GuiFrameSource = subElement;
516 case "alternativelayout":
526 if (filePath.IsNullOrEmpty())
528 DebugConsole.ThrowError(
529 $
"Error when instantiating item \"{item.Name}\" - sound with no file path set",
542 DebugConsole.ThrowError($
"Invalid sound type \"{typeStr}\" in item \"{item.Prefab.Identifier}\"!", e,
547 RoundSound sound = RoundSound.Load(subElement);
548 if (sound ==
null) {
break; }
549 ItemSound itemSound =
new ItemSound(sound, type,
556 if (soundSelectionModes ==
null)
558 soundSelectionModes =
new Dictionary<ActionType, SoundSelectionMode>();
560 if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] ==
SoundSelectionMode.Random)
562 soundSelectionModes[type] = subElement.GetAttributeEnum(
"selectionmode",
SoundSelectionMode.Random);
565 if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList))
567 soundList =
new List<ItemSound>();
568 sounds.Add(itemSound.Type, soundList);
569 hasSoundsOfType[(int)itemSound.Type] =
true;
572 soundList.Add(itemSound);
580 private XElement GuiFrameSource;
597 if (GuiFrameSource.Attribute(
"color") !=
null)
599 color = GuiFrameSource.GetAttributeColor(
"color", Color.White);
601 string style = GuiFrameSource.Attribute(
"style") ==
null ? null : GuiFrameSource.GetAttributeString(
"style",
"");
617 if (
GuiFrame !=
null && GuiFrameSource.GetAttributeBool(
"draggable",
true))
619 bool hideDragIcons = GuiFrameSource.GetAttributeBool(
"hidedragicons",
false);
625 DragArea = HUDLayoutSettings.ItemHUDArea
628 int iconHeight = GUIStyle.ItemFrameMargin.Y / 4;
630 style:
"GUIDragIndicatorHorizontal");
631 dragIcon.RectTransform.MinSize =
new Point(0, iconHeight);
638 if (ic ==
this || ic.GuiFrame ==
null || !ic.CanBeSelected) {
continue; }
646 if (dragIcon.Rect.Intersects(ic.GuiFrame.Rect))
658 guiFrameUpdatePending =
true;
662 int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
664 style:
"GUIButtonSettings")
666 OnClicked = (btn, userdata) =>
668 GUIContextMenu.CreateContextMenu(
673 if (ic.GuiFrame != null && ic.GuiFrameOffset != Point.Zero)
675 ic.GuiFrameOffset = Point.Zero;
676 ic.guiFrameUpdatePending = true;
681 Character.Controlled.SelectedItem.ForceHUDLayoutUpdate(ignoreLocking: true);
685 item.ForceHUDLayoutUpdate(ignoreLocking: true);
690 LockGuiFramePosition = !LockGuiFramePosition;
691 guiFrameDragHandle.Enabled = !LockGuiFramePosition;
692 if (SerializableProperties.TryGetValue(nameof(LockGuiFramePosition).ToIdentifier(), out var property))
694 GameMain.Client?.CreateEntityEvent(Item, new Item.ChangePropertyEventData(property, this));
704 settingsIcon.Visible =
false;
717 if (delayedCorrectionCoroutine !=
null) { CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); }
719 delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(buffer, sendingTime, waitForMidRoundSync));
722 private IEnumerable<CoroutineStatus> DoDelayedCorrection(
IReadMessage buffer,
float sendingTime,
bool waitForMidRoundSync)
727 correctionTimer -= CoroutineManager.DeltaTime;
738 correctionTimer = 0.0f;
739 delayedCorrectionCoroutine =
null;
749 if (newParent ==
null)
758 private void OnResolutionChangedPrivate()
760 if (RecreateGUIOnResolutionChange)
765 OnResolutionChanged();
766 item.ForceHUDLayoutUpdate(ignoreLocking:
true);
769 dragHandle.DragArea = HUDLayoutSettings.ItemHUDArea;
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
static Character? Controlled
string? GetAttributeString(string key, string? def)
ContentPackage? ContentPackage
string GetAttributeStringUnrestricted(string key, string def)
bool GetAttributeBool(string key, bool def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
static CoroutineStatus Running
static CoroutineStatus Success
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
void ImmediateFlash(Color? color=null)
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
RectTransform RectTransform
Func< RectTransform, bool > ValidatePosition
static int GraphicsHeight
Action ResolutionChanged
NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer...
static Sounds.SoundManager SoundManager
virtual void CreateSlots()
IEnumerable< ItemComponent > ActiveHUDs
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
void CheckNeedsSoundUpdate(ItemComponent ic)
List< ItemComponent > Components
static GUILayoutSettings Load(XElement element)
void ApplyTo(RectTransform target)
The base class for components holding the different functionalities of the item
Dictionary< Identifier, SerializableProperty > SerializableProperties
void StartDelayedCorrection(IReadMessage buffer, float sendingTime, bool waitForMidRoundSync=false)
GUILayoutSettings AlternativeLayout
virtual void UpdateEditing(float deltaTime)
virtual void CreateEditingHUD(SerializableEntityEditor editor)
ItemComponent GetLinkUIToComponent()
virtual void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description)
virtual void AddToGUIUpdateList(int order=0)
int ManuallySelectedSound
Which sound should be played when manual sound selection type is selected? Not [Editable] because we ...
void UpdateHUD(Character character, float deltaTime, Camera cam)
virtual void CreateGUI()
Overload this method and implement. The method is automatically called when the resolution changes.
virtual bool ShouldDrawHUD(Character character)
void PlaySound(ActionType type, Character user=null)
virtual void OnPlayerSkillsChanged()
virtual void OnResolutionChanged()
bool LockGuiFramePosition
void TryCreateDragHandle()
virtual void DrawHUD(SpriteBatch spriteBatch, Character character)
bool?? UseAlternativeLayout
ItemComponent GetReplacementOrThis()
virtual void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
void OnGUIParentChanged(RectTransform newParent)
Launches when the parent of the GuiFrame is changed.
void StopSounds(ActionType type)
virtual bool RecreateGUIOnResolutionChange
GUILayoutSettings DefaultLayout
ItemSound(RoundSound sound, ActionType type, bool loop=false, bool onlyPlayInSameSub=false)
Identifier VolumeProperty
readonly bool OnlyPlayInSameSub
readonly RoundSound RoundSound
override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData=null)
float GetRandomFrequencyMultiplier()
readonly bool IgnoreMuffling
virtual SoundChannel Play(float gain, float range, Vector2 position, bool muffle=false)
override Vector2? WorldPosition
bool IsEntityFoundOnThisSub(MapEntity entity, bool includingConnectedSubs, bool allowDifferentTeam=false, bool allowDifferentType=false)
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.