diff --git a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
index bf7ab26cba2..22b76fb9691 100644
--- a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
+++ b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
@@ -1,26 +1,31 @@
using System.Linq;
using Content.Client.Gameplay;
+using Content.Shared._Crescent.SpaceBiomes;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
-using Content.Shared.Random;
-using Content.Shared.Random.Rules;
-using Robust.Client.GameObjects;
+using Content.Client._Crescent.SpaceBiomes;
using Robust.Client.Player;
-using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Shared.Audio;
-using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Content.Client.CombatMode;
+using Content.Shared.CombatMode;
using Robust.Shared.Timing;
-using Robust.Shared.Utility;
+using Content.Shared.NPC.Components;
+using Content.Shared._Mono.CCVar;
+using Content.Shared._Crescent.Vessel;
+using Content.Shared.Ghost;
namespace Content.Client.Audio;
+///
+/// This handles playing ambient music over time, and combat music per faction.
+///
public sealed partial class ContentAudioSystem
{
[Dependency] private readonly IConfigurationManager _configManager = default!;
@@ -29,238 +34,532 @@ public sealed partial class ContentAudioSystem
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IStateManager _state = default!;
- [Dependency] private readonly RulesSystem _rules = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly CombatModeSystem _combatModeSystem = default!; //CLIENT ONE. WHY ARE THERE 3???
+ [Dependency] private readonly SpaceBiomeSystem _spaceBiome = default!;
- private readonly TimeSpan _minAmbienceTime = TimeSpan.FromSeconds(30);
- private readonly TimeSpan _maxAmbienceTime = TimeSpan.FromSeconds(60);
+ //options menu ---
+ private static float _volumeSliderAmbient;
+ private static float _volumeSliderCombat;
+ private static bool _combatMusicToggle;
+ //options menu ---
- private const float AmbientMusicFadeTime = 10f;
- private static float _volumeSlider;
-
- // Don't need to worry about this being serializable or pauseable as it doesn't affect the sim.
- private TimeSpan _nextAudio;
+ private const string NpcFactionPDV = "PirateNF"; //we should really fucking change these on monolith. wtf
+ private const string NpcFactionTSFMC = "TSFMC";
+ // This stores the music stream. It's used to start/stop the music on the fly.
private EntityUid? _ambientMusicStream;
+
+ // This stores the ambient music prototype to be played next.
private AmbientMusicPrototype? _musicProto;
- ///
- /// If we find a better ambient music proto can we interrupt this one.
- ///
- private bool _interruptable;
+ // Time to wait in between replaying ambient music tracks. Should be at least 1-2 seconds to prevent possible overlapping.
+ private float _timeUntilNextAmbientTrack = 1;
- ///
- /// Track what ambient sounds we've played. This is so they all get played an even
- /// number of times.
- /// When we get to the end of the list we'll re-shuffle
- ///
- private readonly Dictionary> _ambientSounds = new();
+ // List of available ambient music tracks to sift through.
+ private List? _musicTracks;
+
+ // Time in seconds for ambient music tracks to fade in. Set to 0 to play immediately.
+ private float _ambientMusicFadeInTime = 10f;
+
+ // Time in seconds for combat music tracks to fade in. Set to 0 to play immediately.
+ private float _combatMusicFadeInTime = 2f;
+
+ // Time that combat mode needs to be on to start playing music. Set to 0 to play immediately.
+ private float _combatMusicTimeToStart;
+
+ // Time that combat mode needs to be off to stop combat mode. Set to 0 to turn off as soon as combat mode is off.
+ private float _combatMusicTimeToEnd;
+
+ // Combat mode state before checking to switch combat music off/on.
+ // 1. We toggle combat mode. We fire SwitchCombatMusic in (timer) seconds.
+ // 2. We save the state from step 1 in _lastCombatState
+ // 3. When SwitchCombatMusic fires, we check if the current combat state is different than _lastCombatState. If it is, then we change music. If not, we keep it.
+ private bool _lastCombatState = false;
+
+ private ProtoId? _lastBiome;
+ private EntityUid? _lastGrid;
+
+ private enum MusicType : byte //used to deal with edgecases of when music should not be overridden
+ {
+ None = 0,
+ Biome = 1,
+ Grid = 2,
+ Combat = 3
+ }
+ private MusicType? _currentlyPlaying = MusicType.None;
+
+ // really stupid - i need this to check if the volume changes when you change the options menu options.
+ private bool _isCombatMusicPlaying = false;
+
+ private float _replayAmbientMusicTimer = 0;
+ private bool _replayAmbientMusicBool;
+ private float _combatWindUpTimer = 0;
+ private bool _combatWindUpBool = false;
+ private float _combatWindDownTimer = 0;
+ private bool _combatWindDownBool = false;
+
+
+ // ok so - the parent change for the station happens too early and something fucks up
+ // so the music system plays the music but you can't hear it. this is for OnPlayerSpawn to set the replayambientmusictimer to replay it, which fixes it
+ // its hacky, but itll do for now. inb4 this line is still here 2 years later
+ private const float InitialStationMusicTimeToDrop = 5f;
+ private float _initialStationMusicTimer = 0f;
+ private bool _initialStationMusicBool = false;
+ private ISawmill _sawmill = default!; //lobbymusic.cs has a sawmill call so i can't remove this????
+
+ private ProtoId _defaultBiomeProto = "BiomeDefault"; //which biome proto is the fallback for null?
+
+ public void UpdateAmbientMusic(float frameTime)
+ {
+ base.Update(frameTime);
- private ISawmill _sawmill = default!;
+ if (!_timing.IsFirstTimePredicted) //otherwise this will tick like 5x faster on client. thanks prediction
+ return;
+
+ if (_initialStationMusicBool)
+ {
+ _initialStationMusicTimer += frameTime;
+ if (_replayAmbientMusicTimer > InitialStationMusicTimeToDrop)
+ {
+ ReplayAmbientMusic();
+ _initialStationMusicBool = false;
+ _initialStationMusicTimer = 0;
+ _replayAmbientMusicTimer = 0;
+ }
+ }
+ if (_replayAmbientMusicBool)
+ {
+ _replayAmbientMusicTimer += frameTime;
+ if (_replayAmbientMusicTimer > _timeUntilNextAmbientTrack)
+ {
+ ReplayAmbientMusic();
+ _replayAmbientMusicTimer = 0;
+ }
+ }
+ if (_combatWindUpBool)
+ {
+ _combatWindUpTimer += frameTime;
+ if (_combatWindUpTimer > _combatMusicTimeToStart)
+ {
+ SwitchCombatMusic(true);
+ _combatWindUpBool = false;
+ _combatWindUpTimer = 0;
+ }
+ }
+ if (_combatWindDownBool)
+ {
+ _combatWindDownTimer += frameTime;
+ if (_combatWindDownTimer > _combatMusicTimeToEnd)
+ {
+ SwitchCombatMusic(false);
+ _combatWindDownBool = false;
+ _combatWindDownTimer = 0;
+ }
+ }
+ }
private void InitializeAmbientMusic()
{
+ SubscribeLocalEvent(OnBiomeChange);
+ SubscribeLocalEvent(OnPlayerParentChange);
+ SubscribeLocalEvent(OnCombatModeToggle);
+
+ SubscribeLocalEvent(OnPlayerDetach); //in case u die in combatmode
+
+ SubscribeLocalEvent(OnPlayerSpawn);
+
Subs.CVar(_configManager, CCVars.AmbientMusicVolume, AmbienceCVarChanged, true);
- _sawmill = IoCManager.Resolve().GetSawmill("audio.ambience");
+ Subs.CVar(_configManager, MonoCVars.CombatMusicVolume, CombatCVarChanged, true);
+ Subs.CVar(_configManager, MonoCVars.CombatMusicEnabled, CombatToggleChanged, true);
+ Subs.CVar(_configManager, MonoCVars.CombatMusicWindUpTime, CombatWindUpChanged, true);
+ Subs.CVar(_configManager, MonoCVars.CombatMusicWindDownTime, CombatWindDownChanged, true);
- // Reset audio
- _nextAudio = TimeSpan.MaxValue;
+ // Setup tracks to pull from. Runs once.
+ _musicTracks = GetTracks();
+
+
+ //no longer needed because we track my the current audio track's time
+ //Timer.Spawn(_timeUntilNextAmbientTrack, () => ReplayAmbientMusic());
- SetupAmbientSounds();
SubscribeLocalEvent(OnProtoReload);
_state.OnStateChanged += OnStateChange;
// On round end summary OR lobby cut audio.
SubscribeNetworkEvent(OnRoundEndMessage);
}
- private void AmbienceCVarChanged(float obj)
+ ///
+ /// Tracks player spawning in to replay ambient music 5 seconds in, making sure station music plays.
+ /// Currently, there is a bug that I can't fix, where after spawning in, station music won't kick in despite the PlayMusic function firing.
+ ///
+ ///
+ private void OnPlayerSpawn(LocalPlayerAttachedEvent ev)
{
- _volumeSlider = SharedAudioSystem.GainToVolume(obj);
+ _initialStationMusicBool = true;
+ _initialStationMusicTimer = 0f;
- if (_ambientMusicStream != null && _musicProto != null)
- {
- _audio.SetVolume(_ambientMusicStream, _musicProto.Sound.Params.Volume + _volumeSlider);
- }
}
- private void ShutdownAmbientMusic()
- {
- _state.OnStateChanged -= OnStateChange;
- _ambientMusicStream = _audio.Stop(_ambientMusicStream);
- }
- private void OnProtoReload(PrototypesReloadedEventArgs obj)
+ ///
+ /// This makes sure that combatmode turns OFF when u ghost, or when ur in aghost and return to ur character.
+ ///
+ ///
+ private void OnPlayerDetach(LocalPlayerDetachedEvent ev)
{
- if (obj.WasModified() || obj.WasModified())
- SetupAmbientSounds();
+ SetMusic(_lastGrid, _lastBiome, false);
}
- private void OnStateChange(StateChangedEventArgs obj)
- {
- if (obj.NewState is not GameplayState)
- return;
- // If they go to game then reset the ambience timer.
- _nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
- }
-
- private void SetupAmbientSounds()
+ ///
+ /// Helper function to replay ambient music after it's done.
+ ///
+ private void ReplayAmbientMusic()
{
- _ambientSounds.Clear();
- foreach (var ambience in _proto.EnumeratePrototypes())
+ if (_musicProto == null) //if we don't find any, we play the default track.
{
- var tracks = _ambientSounds.GetOrNew(ambience.ID);
- RefreshTracks(ambience.Sound, tracks, null);
- _random.Shuffle(tracks);
+ _musicProto = _proto.Index(_defaultBiomeProto);
+ _lastBiome = _proto.Index(_defaultBiomeProto);
}
+
+ SoundCollectionPrototype soundcol = _proto.Index(_musicProto.ID);
+
+ string path = _random.Pick(soundcol.PickFiles).ToString(); //picks a random track. if someone really cared we could make it make sure it doesnt play the same track twice
+
+ PlayMusicTrack(path, _musicProto.Sound.Params.Volume, _ambientMusicFadeInTime, false);
}
- private void OnRoundEndMessage(RoundEndMessageEvent ev)
+ private void OnBiomeChange(ref SpaceBiomeSwapMessage ev)
{
- // If scoreboard shows then just stop the music
- _ambientMusicStream = _audio.Stop(_ambientMusicStream);
- _nextAudio = TimeSpan.FromMinutes(3);
+ SetMusic(_lastGrid, ev.Id, _lastCombatState);
}
-
- private void RefreshTracks(SoundSpecifier sound, List tracks, ResPath? lastPlayed)
+ private void OnPlayerParentChange(ref PlayerParentChangedMessage ev)
{
- DebugTools.Assert(tracks.Count == 0);
-
- switch (sound)
+ SetMusic(ev.Grid, _lastBiome, _lastCombatState);
+ }
+ private void SwitchCombatMusic(bool currentCombatState)
+ {
+ SetMusic(_lastGrid, _lastBiome, currentCombatState);
+ }
+ private void OnCombatModeToggle(ToggleCombatActionEvent ev)
+ {
+ if (_combatMusicToggle == false) // if cvar is off, don't bother
+ return;
+ if (!_timing.IsFirstTimePredicted == true) //needed, because combat mode is predicted, and triggers 7 times otherwise.
+ return;
+ bool currentCombatState = _combatModeSystem.IsInCombatMode();
+ if (currentCombatState) //if combat mode is being turned ON
{
- case SoundCollectionSpecifier collection:
- if (collection.Collection == null)
- break;
-
- var slothCud = _proto.Index(collection.Collection);
- tracks.AddRange(slothCud.PickFiles);
- break;
- case SoundPathSpecifier path:
- tracks.Add(path.Path);
- break;
+ _combatWindUpBool = true;
+ _combatWindUpTimer = 0;
+ _combatWindDownBool = false;
+ _combatWindDownTimer = 0;
}
-
- // Just so the same track doesn't play twice
- if (tracks.Count > 1 && tracks[^1] == lastPlayed)
+ else //if combat mode is being turned OFF
{
- (tracks[0], tracks[^1]) = (tracks[^1], tracks[0]);
+ _combatWindDownBool = true;
+ _combatWindDownTimer = 0;
+ _combatWindUpBool = false;
+ _combatWindUpTimer = 0;
}
}
- private void UpdateAmbientMusic()
+ ///
+ /// Function that takes in data from requests to change music and determines what music to play / if it should play it.
+ ///
+ ///
+ ///
+ ///
+ private void SetMusic(EntityUid? newGrid, ProtoId? newBiome, bool newCombatState)
{
- // Update still runs in lobby so just ignore it.
- if (_state.CurrentState is not GameplayState)
+ //Log.Info("SETMUSIC: - GRID: " + newGrid.ToString() + " BIOME: " + newBiome.ToString() + " COMBAT: " + newCombatState.ToString());
+ // priority list:
+ // 1. (not implemented yet :godo:) ship combat music
+ // 2. combat music
+ // 3. grid music
+ // 4. biome music
+ // therefore we check these top 2 bottom
+
+ #region combat music
+ if (newCombatState != _lastCombatState) //we switch combat music on or off now
{
- _ambientMusicStream = Audio.Stop(_ambientMusicStream);
- _musicProto = null;
+ _lastCombatState = newCombatState; // cache combat state since its different than the last
+ if (newCombatState) //true = we toggled combat ON.
+ {
+ // figure out the faction we should play combat music for
+ string factionComponentString = "";
+ if (TryComp(_player.LocalEntity, out NpcFactionMemberComponent? factionComp))
+ factionComponentString = factionComp.Factions.FirstOrDefault("");
+ string combatFactionSuffix; //this is added to "combatmode" to create "combatmodePDV", etc, to fetch combat tracks.
+ switch (factionComponentString) //this will hardcode the valid factions but until someone cleans up the frontier tags this looks way nicer
+ {
+ case NpcFactionPDV:
+ combatFactionSuffix = "PDV";
+ break;
+ case NpcFactionTSFMC:
+ combatFactionSuffix = "TSFMC";
+ break;
+ default:
+ combatFactionSuffix = "Default";
+ break;
+ }
+
+ // if we find a ambient music prototype for our faction, then pick that one!
+ if (_proto.TryIndex("CombatMode" + combatFactionSuffix, out var factionCombatMusicPrototype))
+ _musicProto = factionCombatMusicPrototype;
+ else //if we don't ,set it to the default
+ _musicProto = _proto.Index("CombatModeDefault");
+
+ _currentlyPlaying = MusicType.Combat;
+
+ SoundCollectionPrototype soundcol = _proto.Index(_musicProto.ID);
+ string path = _random.Pick(soundcol.PickFiles).ToString();
+ PlayMusicTrack(path, _musicProto.Sound.Params.Volume, _combatMusicFadeInTime, true);
+ return;
+ }
+ else
+ {
+ //false = we toggled combat OFF, therefore we should play music from our other data we have in this current request.
+ // the easiest way to do this is to set lastgrid & lastbiome to null.
+ _currentlyPlaying = MusicType.None;
+ _lastBiome = null;
+ _lastGrid = null;
+ }
+ }
+ #endregion
+
+ if (_currentlyPlaying >= MusicType.Combat) //if we are in combatmode, we still want to cache info, but we want to return here so that we dont stop playing combatmode music
+ {
+ _lastGrid = newGrid;
+ _lastBiome = newBiome;
return;
}
- bool? isDone = null;
+ #region grid music
- if (TryComp(_ambientMusicStream, out AudioComponent? audioComp))
+ if (newGrid != _lastGrid || _currentlyPlaying != MusicType.Grid)
{
- isDone = !audioComp.Playing;
+ if (newGrid != null && TryComp(newGrid, out var music)) //do we have grid music? also this gives false if null
+ {
+ _lastGrid = newGrid;
+ _lastBiome = newBiome;
+ _currentlyPlaying = MusicType.Grid;
+
+ _musicProto = _proto.Index(music.AmbientMusicPrototype);
+ SoundCollectionPrototype soundcol = _proto.Index(_musicProto.ID);
+ string path = _random.Pick(soundcol.PickFiles).ToString();
+ PlayMusicTrack(path, _musicProto.Sound.Params.Volume, _ambientMusicFadeInTime, false);
+ return;
+ }
+ else
+ {
+ // pass onto next
+ }
}
-
- if (_interruptable)
+ else
{
- var player = _player.LocalSession?.AttachedEntity;
+ _lastGrid = newGrid;
+ _lastBiome = newBiome;
+ return;
+ }
+
+ #endregion
+ #region biome music
- if (player == null || _musicProto == null || !_rules.IsTrue(player.Value, _proto.Index(_musicProto.Rules)))
+ if (_lastBiome != newBiome || _currentlyPlaying != MusicType.Biome) //if newBiome is null, we go to fallback
+ {
+ if (newBiome == null)
{
- FadeOut(_ambientMusicStream, duration: AmbientMusicFadeTime);
+ _musicProto = _proto.Index(_defaultBiomeProto);
+ }
+ else
+ {
+ if (_musicTracks == null) // if this is null we have way bigger issues
+ return;
_musicProto = null;
- _interruptable = false;
- isDone = true;
+ //else
+ foreach (var ambient in _musicTracks)
+ {
+ if (newBiome.Value.Id == ambient.ID) //if we find the biome that's matching an ambientMusic prototype's ID, we play that set.
+ {
+ _musicProto = ambient;
+ break;
+ }
+ }
+ if (_musicProto == null) //if we don't find any ambient music matching our current biome in _musicTracks, we play the fallback track.
+ {
+ _musicProto = _proto.Index(_defaultBiomeProto);
+ }
}
- }
- // Still running existing ambience
- if (isDone == false)
- return;
+ _lastBiome = newBiome; // update cache
+ _lastGrid = newGrid;
+ _currentlyPlaying = MusicType.Biome;
- // If ambience finished reset the CD (this also means if we have long ambience it won't clip)
- if (isDone == true)
+ SoundCollectionPrototype soundcol = _proto.Index(_musicProto.ID);
+ string path = _random.Pick(soundcol.PickFiles).ToString();
+ PlayMusicTrack(path, _musicProto.Sound.Params.Volume, _ambientMusicFadeInTime, false);
+ return;
+ }
+ else
{
- // Also don't need to worry about rounding here as it doesn't affect the sim
- _nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
+ _lastGrid = newGrid;
+ _lastBiome = newBiome;
+ return;
}
- _ambientMusicStream = null;
+ #endregion
+ }
- if (_nextAudio > _timing.CurTime)
- return;
- _musicProto = GetAmbience();
+ ///
+ /// Helper function that sets up parameters, audio stream, fadein, and music volume.
+ ///
+ /// Path to music to play.
+ /// Volume modifier (put 0 to keep original volume).
+ /// Seconds for the music to fade in. Put 0 for no fadein.
+ private void PlayMusicTrack(string path, float volume, float fadein, bool combatMode)
+ {
+ _isCombatMusicPlaying = combatMode;
+ FadeOut(_ambientMusicStream);
- if (_musicProto == null)
+ if (combatMode)
{
- _interruptable = false;
- return;
+ volume += _volumeSliderCombat;
+ _replayAmbientMusicBool = false;
+ }
+ else
+ {
+ volume += _volumeSliderAmbient;
+ _replayAmbientMusicBool = true;
}
-
- _interruptable = _musicProto.Interruptable;
- var tracks = _ambientSounds[_musicProto.ID];
-
- var track = tracks[^1];
- tracks.RemoveAt(tracks.Count - 1);
var strim = _audio.PlayGlobal(
- track.ToString(),
+ path,
Filter.Local(),
false,
- AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider));
+ AudioParams.Default.WithVolume(volume))!;
- _ambientMusicStream = strim?.Entity;
+ _ambientMusicStream = strim.Value.Entity; //this plays it immediately, but fadein function later makes it actually fade in.
- if (_musicProto.FadeIn && strim != null)
+ if (fadein != 0)
+ FadeIn(_ambientMusicStream, strim.Value.Component, fadein);
+
+ _timeUntilNextAmbientTrack = (float)_audio.GetAudioLength(path).TotalSeconds;
+ }
+
+ ///
+ /// Helper function that fetches music tracks to set up the list to pull from on initialize.
+ ///
+ ///
+ ///
+ private List GetTracks()
+ {
+ List musictracks = new List();
+
+ bool fallback = true;
+ foreach (var ambience in _proto.EnumeratePrototypes())
{
- FadeIn(_ambientMusicStream, strim.Value.Component, AmbientMusicFadeTime);
+ musictracks.Add(ambience);
+ fallback = false;
}
- // Refresh the list
- if (tracks.Count == 0)
+ if (fallback) //if we somehow FOUND NO MUSIC TRACKS
{
- RefreshTracks(_musicProto.Sound, tracks, track);
+ throw new NullReferenceException("found no music tracks defined");
}
- }
- private AmbientMusicPrototype? GetAmbience()
+ return musictracks;
+ }
+ private void AmbienceCVarChanged(float obj)
{
- var player = _player.LocalEntity;
+ _volumeSliderAmbient = SharedAudioSystem.GainToVolume(obj);
- if (player == null)
- return null;
+ //this changes the music volume live, while the music is playing. otherwise, the line above that changes the slider is the one that matters.
- var ev = new PlayAmbientMusicEvent();
- RaiseLocalEvent(ref ev);
+ if (_ambientMusicStream != null && _musicProto != null && !_isCombatMusicPlaying)
+ {
+ _audio.SetVolume(_ambientMusicStream, _musicProto.Sound.Params.Volume + _volumeSliderAmbient);
+ }
+ }
- if (ev.Cancelled)
- return null;
+ private void CombatCVarChanged(float obj)
+ {
+ _volumeSliderCombat = SharedAudioSystem.GainToVolume(obj);
- var ambiences = _proto.EnumeratePrototypes().ToList();
- ambiences.Sort((x, y) => y.Priority.CompareTo(x.Priority));
+ //this changes the music volume live, while the music is playing. otherwise, the line above that changes the slider is the one that matters.
- foreach (var amb in ambiences)
+ if (_ambientMusicStream != null && _musicProto != null && _isCombatMusicPlaying)
{
- if (!_rules.IsTrue(player.Value, _proto.Index(amb.Rules)))
- continue;
-
- return amb;
+ _audio.SetVolume(_ambientMusicStream, _musicProto.Sound.Params.Volume + _volumeSliderCombat);
}
-
- _sawmill.Warning($"Unable to find fallback ambience track");
- return null;
+ }
+ private void CombatWindUpChanged(int obj)
+ {
+ _combatMusicTimeToStart = obj;
+ }
+ private void CombatWindDownChanged(int obj)
+ {
+ _combatMusicTimeToEnd = obj;
}
///
- /// Fades out the current ambient music temporarily.
+ /// Handles the combat mode music cvar being toggled on/off.
///
+ ///
+ private void CombatToggleChanged(bool obj)
+ {
+ _combatMusicToggle = obj;
+
+ if (_combatMusicToggle) // if the player turned combat music back ON, then we don't really care anymore and the system works as usual
+ return;
+ if (_state.CurrentState is not GameplayState)
+ return; //catches this throwing a null reference exception if u had this cvar toggled, in lobby
+ //cuz this next setmusic plays music. and well. we havent collected the music yet when we join in
+ //and we dont need to change music if we're not ingame anyway
+
+ //otherwise we should kill combat music thats playing rn if they turned it off, otherwise it gets STUCK on.
+ SetMusic(_lastGrid, _lastBiome, false);
+ }
+
+ private void ShutdownAmbientMusic()
+ {
+ _state.OnStateChanged -= OnStateChange;
+ _ambientMusicStream = _audio.Stop(_ambientMusicStream);
+ }
+
+ private void OnProtoReload(PrototypesReloadedEventArgs obj)
+ {
+ if (obj.WasModified())
+ _musicTracks = GetTracks();
+ }
+ ///
+ /// This function handles the change from lobby to gameplay, disabling music when you're not in gameplay state.
+ ///
+ private void OnStateChange(StateChangedEventArgs obj)
+ {
+ if (obj.NewState is not GameplayState)
+ DisableAmbientMusic();
+ }
+
+ private void OnRoundEndMessage(RoundEndMessageEvent ev)
+ {
+ if (_ambientMusicStream == null)
+ {
+ //_sawmill.Debug("AMBIENT MUSIC STREAM WAS NULL? FROM OnRoundEndMessage()");
+ return;
+ }
+ // If scoreboard shows then just stop the music
+ _ambientMusicStream = _audio.Stop(_ambientMusicStream);
+ }
public void DisableAmbientMusic()
{
+ if (_ambientMusicStream == null)
+ {
+ return;
+ }
FadeOut(_ambientMusicStream);
_ambientMusicStream = null;
}
+
}
diff --git a/Content.Client/Audio/ContentAudioSystem.cs b/Content.Client/Audio/ContentAudioSystem.cs
index 6fe9b5562db..83824036e21 100644
--- a/Content.Client/Audio/ContentAudioSystem.cs
+++ b/Content.Client/Audio/ContentAudioSystem.cs
@@ -32,6 +32,7 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
public const float TtsMultiplier = 3f; // Corvax-TTS
public const float SalvageMultiplier = 1f; // Frontier
+ public const float CombatMultiplier = 3f; //Mono
public override void Initialize()
{
@@ -84,7 +85,7 @@ public override void Update(float frameTime)
if (!_timing.IsFirstTimePredicted)
return;
- UpdateAmbientMusic();
+ UpdateAmbientMusic(frameTime);
UpdateLobbyMusic();
UpdateFades(frameTime);
}
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml b/Content.Client/Options/UI/Tabs/AudioTab.xaml
index ed04d42d349..6ed05e64ea5 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml
@@ -20,6 +20,9 @@
+
+
+
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
index 2262f147565..94fa4c8351c 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
@@ -88,6 +88,28 @@ public AudioTab()
SliderVolumeTtsRadio,
scale: ContentAudioSystem.TtsMultiplier);
+ // Mono begin
+ Control.AddOptionPercentSlider(
+ MonoCVars.CombatMusicVolume,
+ SliderVolumeCombatMusic,
+ scale: ContentAudioSystem.CombatMultiplier);
+
+ Control.AddOptionSlider(
+ MonoCVars.CombatMusicWindUpTime,
+ SliderWindUpCombatMusic,
+ 1,
+ 10,
+ (_, value) => Loc.GetString("ui-options-combat-music-sec-format", ("value", value))); // i dont think anyone is ghonna want more than 10 seconds of constant combatmode being the trigger. the actions already over
+
+ Control.AddOptionSlider(
+ MonoCVars.CombatMusicWindDownTime,
+ SliderWindDownCombatMusic,
+ 1,
+ 120,
+ (_, value) => Loc.GetString("ui-options-combat-music-sec-format", ("value", value))); // and also i think 2 minutes is enough of an upper limit since it refreshes anytime you turn it back on even for a second
+
+ Control.AddOptionCheckBox(MonoCVars.CombatMusicEnabled, CombatMusicCheckBox);
+ // Mono end
Control.AddOptionCheckBox(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox);
Control.AddOptionCheckBox(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox);
Control.AddOptionCheckBox(CCVars.EventMusicEnabled, EventMusicCheckBox);
diff --git a/Content.Client/Salvage/SalvageExpeditionComponent.cs b/Content.Client/Salvage/SalvageExpeditionComponent.cs
index b916462d140..ff7248c1a17 100644
--- a/Content.Client/Salvage/SalvageExpeditionComponent.cs
+++ b/Content.Client/Salvage/SalvageExpeditionComponent.cs
@@ -1,12 +1,15 @@
+using Content.Shared._Crescent.SpaceBiomes;
using Content.Shared.Salvage.Expeditions;
+using Robust.Shared.Prototypes;
namespace Content.Client.Salvage;
[RegisterComponent]
public sealed partial class SalvageExpeditionComponent : SharedSalvageExpeditionComponent
{
- // Frontier: add audio stream
+ ///
+ /// Mono: used for ContentAudioSystem.AmbientMusic.cs & SpaceBiomeSystem.cs to communicate biome on FTL
+ ///
[DataField]
- public EntityUid? Stream;
- // End Frontier
+ public ProtoId Biome = "BiomeExpedition";
}
diff --git a/Content.Client/Salvage/SalvageSystem.cs b/Content.Client/Salvage/SalvageSystem.cs
index 35b51e7c70f..e1bce367cae 100644
--- a/Content.Client/Salvage/SalvageSystem.cs
+++ b/Content.Client/Salvage/SalvageSystem.cs
@@ -3,11 +3,6 @@
using Content.Shared.Salvage.Expeditions;
using Robust.Client.Player;
using Robust.Shared.GameStates;
-using Content.Shared._NF.CCVar; // Frontier
-using Robust.Client.Audio; // Frontier
-using Robust.Shared.Audio; // Frontier
-using Robust.Shared.Configuration; // Frontier
-using Robust.Shared.Player; // Frontier
namespace Content.Client.Salvage;
@@ -15,19 +10,12 @@ public sealed class SalvageSystem : SharedSalvageSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly ContentAudioSystem _audio = default!;
- [Dependency] private readonly AudioSystem _audioSystem = default!; // Frontier
- [Dependency] private readonly IConfigurationManager _cfg = default!; // Frontier
-
- const float SalvageExpeditionMinMusicVolume = -30f; // Frontier: expedition volume range
- const float SalvageExpeditionMaxMusicVolume = 3.0f; // Frontier: expedition volume range
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnPlayAmbientMusic);
SubscribeLocalEvent(OnExpeditionHandleState);
- SubscribeLocalEvent(OnRemove); // Frontier
- Subs.CVar(_cfg, NFCCVars.SalvageExpeditionMusicVolume, SetMusicVolume); // Frontier
}
private void OnExpeditionHandleState(EntityUid uid, SalvageExpeditionComponent component, ref ComponentHandleState args)
@@ -36,27 +24,11 @@ private void OnExpeditionHandleState(EntityUid uid, SalvageExpeditionComponent c
return;
component.Stage = state.Stage;
- if (state.SelectedSong != null) // Frontier
- component.SelectedSong = state.SelectedSong; // Frontier
if (component.Stage >= ExpeditionStage.MusicCountdown)
{
_audio.DisableAmbientMusic();
}
-
- // Frontier: add music (only on music countdown, no music on forced exit)
- if (component.Stage == ExpeditionStage.MusicCountdown
- && component.SelectedSong != null
- && component.Stream == null)
- {
- var volume = ConvertSliderValueToVolume(_cfg.GetCVar(NFCCVars.SalvageExpeditionMusicVolume));
- var audioParams = AudioParams.Default.WithVolume(volume);
- var audio = _audioSystem.PlayEntity(component.SelectedSong, Filter.Local(), uid, false, audioParams);
- _audioSystem.SetMapAudio(audio);
-
- component.Stream = audio?.Entity;
- }
- // End Frontier
}
private void OnPlayAmbientMusic(ref PlayAmbientMusicEvent ev)
@@ -75,35 +47,4 @@ private void OnPlayAmbientMusic(ref PlayAmbientMusicEvent ev)
ev.Cancelled = true;
}
-
- // Frontier: stop stream when destroying the expedition
- private void OnRemove(EntityUid uid, SalvageExpeditionComponent component, ComponentRemove args)
- {
- // For whatever reason, this stream is considered a server-side entity, so the AudioSystem won't tear it down.
- // Don't really understand why, but I don't think it is.
-
- //component.Stream = _audioSystem.Stop(component.Stream);
- QueueDel(component.Stream);
- }
-
- private void SetMusicVolume(float volume)
- {
- var expedQuery = EntityQueryEnumerator();
- while (expedQuery.MoveNext(out _, out var comp))
- {
- if (comp.Stream != null)
- _audioSystem.SetVolume(comp.Stream, ConvertSliderValueToVolume(volume));
- }
- }
-
- private float ConvertSliderValueToVolume(float value)
- {
- var ret = AudioSystem.GainToVolume(value);
- if (!float.IsFinite(ret)) // Explicitly handle any odd cases (chiefly NaN)
- ret = SalvageExpeditionMinMusicVolume;
- else
- ret = Math.Clamp(ret, SalvageExpeditionMinMusicVolume, SalvageExpeditionMaxMusicVolume);
- return ret;
- }
- // End Frontier: stop stream when destroying the expedition
}
diff --git a/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeMessages.cs b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeMessages.cs
new file mode 100644
index 00000000000..8dfeb15f8b9
--- /dev/null
+++ b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeMessages.cs
@@ -0,0 +1,26 @@
+using Content.Shared._Crescent.SpaceBiomes;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Client._Crescent.SpaceBiomes;
+
+///
+/// used by space biome system to signal when the player has changed biomes, to check for biome music
+///
+///
+[ByRefEvent]
+public readonly record struct SpaceBiomeSwapMessage(ProtoId Id);
+
+///
+/// used by space biome system to signal when the player's parent is changed, to check for grid music
+///
+///
+[ByRefEvent]
+public readonly record struct PlayerParentChangedMessage(EntityUid? Grid); //null = space
+
+///
+/// used by space biome system to add biome components to dynamically-created maps, like FTLmap and expeditions
+///
+///
+[ByRefEvent]
+public record struct SpaceBiomeMapChangeMessage(ProtoId? Biome);
diff --git a/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeSystem.cs b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeSystem.cs
new file mode 100644
index 00000000000..b980d2f2a28
--- /dev/null
+++ b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeSystem.cs
@@ -0,0 +1,115 @@
+using Content.Shared._Crescent.SpaceBiomes;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+using Robust.Client.Player;
+using Robust.Client.GameObjects;
+using Content.Shared.Shuttles.Components;
+using Content.Client.Salvage;
+
+namespace Content.Client._Crescent.SpaceBiomes;
+
+public sealed class SpaceBiomeSystem : EntitySystem
+{
+ [Dependency] private readonly IPlayerManager _playerMan = default!;
+ [Dependency] private readonly IPrototypeManager _protMan = default!;
+ [Dependency] private readonly TransformSystem _formSys = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IEntityManager _entMan = default!;
+
+ private float _updTimer;
+ private const float UpdateInterval = 0.5f; // in seconds - how often the checks for this system run
+
+ private SpaceBiomeSourceComponent? _cachedSource;
+ private EntityUid? _cachedGrid;
+ private EntityUid? _cachedMap;
+
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnFTLMapChanged);
+ SubscribeLocalEvent(OnSalvageMapChanged);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ if (!_timing.IsFirstTimePredicted) //otherwise this will tick like 5x faster on client. thanks prediction
+ return;
+
+ //update timers
+ _updTimer += frameTime;
+ if (_updTimer < UpdateInterval)
+ return;
+ _updTimer -= UpdateInterval;
+
+ // 0. grab the local player ent
+ if (_playerMan.LocalEntity == null) //this should never be null i thinky
+ return;
+
+ var localPlayerUid = _playerMan.LocalEntity.Value;
+ var xform = Transform(localPlayerUid);
+ var ourCoord = xform.Coordinates;
+
+ // 1. grab the local grid, if any. if not, send msg signalling we entered space
+ var newGrid = xform.GridUid;
+
+ if (newGrid != _cachedGrid) //if true, we have changed grids since last update
+ {
+ _cachedGrid = newGrid;
+ var message = new PlayerParentChangedMessage(newGrid); //if this is null it notifies that we're in space
+ RaiseLocalEvent(localPlayerUid, ref message, true);
+
+ }
+ // 2. grab the biome & check if its different than the cached biome from last update
+ SpaceBiomeSourceComponent? newSource = null;
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var sourceUid, out var comp))
+ {
+ var otherCoord = Transform(sourceUid).Coordinates;
+ if (!ourCoord.TryDistance(EntityManager, otherCoord, out var distance) || distance > (comp.SwapDistance ?? float.MaxValue)) //we're too far from this source, move on
+ continue;
+
+ if (newSource == null || //this whole shebang picks the highest priority source from the EQE
+ comp.Priority > newSource.Priority ||
+ comp.Priority == newSource.Priority && comp == _cachedSource)
+ {
+ newSource = comp;
+ }
+ }
+ // 3. check the mapid and check if its different than the cached mapid from the last update
+ EntityUid? newMap = _formSys.GetMap(localPlayerUid);
+ // 4. this is the actual checking bit
+ // if the map changed then it cant be the same source from last update, so we do _cachedSource = newSource anyway.
+ if (_cachedMap != newMap || _cachedSource != newSource)
+ {
+ var mapSwapMsg = new SpaceBiomeMapChangeMessage();
+ if (newMap != null) //if the new map is null then :godo: we are borked anyway
+ {
+ RaiseLocalEvent((EntityUid)newMap, ref mapSwapMsg, true);
+ }
+ _cachedMap = newMap;
+ _cachedSource = newSource;
+ SpaceBiomePrototype biome;
+ if (mapSwapMsg.Biome != null)
+ biome = _protMan.Index(mapSwapMsg.Biome);
+ else
+ biome = _protMan.Index(newSource?.Id ?? "BiomeDefault");
+ //note: this is where the parallax should swap. eventually.
+ var biomeSwapMsg = new SpaceBiomeSwapMessage(biome);
+ RaiseLocalEvent(localPlayerUid, ref biomeSwapMsg, true);
+
+ }
+ }
+
+ private void OnFTLMapChanged(Entity ent, ref SpaceBiomeMapChangeMessage args)
+ {
+ args.Biome = ent.Comp.Biome;
+ }
+
+ private void OnSalvageMapChanged(Entity ent, ref SpaceBiomeMapChangeMessage args)
+ {
+ args.Biome = ent.Comp.Biome;
+ }
+}
diff --git a/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextDisplaySystem.cs b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextDisplaySystem.cs
new file mode 100644
index 00000000000..ef0bfe51e4a
--- /dev/null
+++ b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextDisplaySystem.cs
@@ -0,0 +1,68 @@
+using Content.Shared._Crescent.SpaceBiomes;
+using Robust.Shared.Prototypes;
+using Content.Client.Audio;
+using Robust.Client.Graphics;
+using Robust.Shared.Timing;
+using Content.Shared._Crescent.Vessel;
+
+namespace Content.Client._Crescent.SpaceBiomes;
+
+public sealed class SpaceTextDisplaySystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _protMan = default!;
+ [Dependency] private readonly IOverlayManager _overMan = default!;
+ [Dependency] private readonly ContentAudioSystem _audioSys = default!;
+
+ private SpaceBiomeTextOverlay _overlay = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnSwap);
+ SubscribeLocalEvent(OnNewVesselEntered);
+ _overlay = new();
+ _overMan.AddOverlay(_overlay);
+ }
+
+ private void OnSwap(ref SpaceBiomeSwapMessage ev)
+ {
+ _audioSys.DisableAmbientMusic();
+ SpaceBiomePrototype biome = _protMan.Index(ev.Id);
+ _overlay.Reset();
+ _overlay.ResetDescription();
+ _overlay.Text = biome.Name;
+ _overlay.TextDescription = biome.Description;
+ _overlay.CharInterval = TimeSpan.FromSeconds(2f / biome.Name.Length);
+ if (_overlay.TextDescription == "") //if we have a biome with no description, it's default is "" and that has length 0.
+ _overlay.CharIntervalDescription = TimeSpan.Zero; //we need to calculate it here because otherwise...
+ else
+ _overlay.CharIntervalDescription = TimeSpan.FromSeconds(2f / biome.Description.Length); //this would throw an exception
+ }
+
+ private void OnNewVesselEntered(ref PlayerParentChangedMessage ev)
+ {
+ if (ev.Grid == null) //player walked into space so we dont care
+ return;
+
+ var name = MetaData((EntityUid)ev.Grid).EntityName; //this should never be null. i hope
+ var description = ""; //fallback for description is nothin'
+ if (TryComp((EntityUid)ev.Grid, out var vesselinfo))
+ description = vesselinfo.Description;
+
+
+ _overlay.Reset(); //these should be reset as well to match OnSwap
+ _overlay.ResetDescription();
+
+ if (_overlay.Text != null) //i dont know why this is here but im not touching it
+ return;
+
+ _overlay.Text = name;
+ _overlay.TextDescription = description; // fallback is "" if no description is found.
+ _overlay.CharInterval = TimeSpan.FromSeconds(2f / _overlay.Text.Length);
+
+ if (_overlay.TextDescription == "")
+ _overlay.CharIntervalDescription = TimeSpan.Zero; //if this is not done it tries dividing by 0 in the "else" clause
+ else
+ _overlay.CharIntervalDescription = TimeSpan.FromSeconds(2f / _overlay.TextDescription.Length);
+ }
+}
diff --git a/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextOverlay.cs b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextOverlay.cs
new file mode 100644
index 00000000000..ed3c9185611
--- /dev/null
+++ b/Content.Client/_Crescent/SpaceBiomes/SpaceBiomeTextOverlay.cs
@@ -0,0 +1,168 @@
+using System.ComponentModel;
+using System.Numerics;
+using System.Runtime.ExceptionServices;
+using System.Text;
+using Content.Client.Resources;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Shared.Enums;
+using Robust.Shared.Timing;
+
+namespace Content.Client._Crescent.SpaceBiomes;
+
+///
+/// this system handles the actual drawing of grid names, descriptions, and biome overlays & descriptions
+///
+public sealed class SpaceBiomeTextOverlay : Overlay
+{
+ [Dependency] private IResourceCache _cache = default!;
+ [Dependency] private IGameTiming _timing = default!;
+
+ public override OverlaySpace Space => OverlaySpace.ScreenSpace;
+ private Font _font;
+ private Font _descriptionfont;
+
+ public string? Text;
+ public int Index;
+ public bool Reverse;
+ public Vector2 Position;
+ public TimeSpan CharInterval;
+ private TimeSpan _nextUpd = TimeSpan.Zero;
+
+ public string? TextDescription;
+ public TimeSpan CharIntervalDescription;
+ public int IndexDescription;
+ public bool ReverseDescription;
+ public Vector2 PositionDescription;
+ private TimeSpan _nextUpdDescription = TimeSpan.Zero;
+
+ public SpaceBiomeTextOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _font = _cache.GetFont("/Fonts/Doloto/Doloto-Regular.ttf", 75); //Lua Iceberg -> Doloto
+ _descriptionfont = _cache.GetFont("/Fonts/Doloto/Doloto-Regular.ttf", 30); //Lua Iceberg -> Doloto
+ }
+
+ public void Reset()
+ {
+ Text = null;
+ Index = 0;
+ Reverse = false;
+ Position = Vector2.Zero;
+ _nextUpd = TimeSpan.Zero;
+ }
+ public void ResetDescription()
+ {
+ TextDescription = null;
+ IndexDescription = 0;
+ ReverseDescription = false;
+ PositionDescription = Vector2.Zero;
+ _nextUpdDescription = TimeSpan.Zero;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (Text == null || Text == string.Empty)
+ return;
+
+ DrawDescription(args); //if there is no description, this returns almost immediately so it shouldnt interfere
+
+ if (Position == Vector2.Zero)
+ Position = CalcPosition(_font, Text, new Vector2(args.ViewportBounds.Width, args.ViewportBounds.Height));
+
+ args.ScreenHandle.DrawString(_font, Position, Text[..Index]);
+
+ if (_nextUpd > _timing.CurTime)
+ return;
+
+ if (!Reverse && Index == Text.Length)
+ {
+ Reverse = true;
+
+ //delay before text is erased
+ _nextUpd += TimeSpan.FromSeconds(2);
+ Index++;
+ }
+
+ if (Reverse && Index == 0)
+ {
+ Reset();
+ return;
+ }
+
+ Index = Reverse ? Index - 1 : Index + 1;
+
+ if (_nextUpd == TimeSpan.Zero)
+ _nextUpd = _timing.CurTime;
+ _nextUpd += CharInterval;
+ }
+
+ private void DrawDescription(OverlayDrawArgs args)
+ {
+ if (TextDescription == null || TextDescription == string.Empty)
+ return;
+
+
+ if (PositionDescription == Vector2.Zero)
+ PositionDescription = CalcPositionDescription(_descriptionfont, TextDescription, new Vector2(args.ViewportBounds.Width, args.ViewportBounds.Height));
+
+
+ args.ScreenHandle.DrawString(_descriptionfont, PositionDescription, TextDescription[..IndexDescription], Color.DarkGray);
+
+ if (_nextUpdDescription > _timing.CurTime)
+ return;
+
+ if (!ReverseDescription && IndexDescription == TextDescription.Length)
+ {
+ ReverseDescription = true;
+
+ //delay before description is erased
+ _nextUpdDescription += TimeSpan.FromSeconds(2);
+ IndexDescription++;
+ }
+
+ if (ReverseDescription && IndexDescription == 0)
+ {
+ ResetDescription();
+ return;
+ }
+
+ IndexDescription = ReverseDescription ? IndexDescription - 1 : IndexDescription + 1;
+
+ if (_nextUpdDescription == TimeSpan.Zero)
+ _nextUpdDescription = _timing.CurTime;
+ _nextUpdDescription += CharIntervalDescription;
+ }
+
+ private Vector2 CalcPosition(Font font, string str, Vector2 viewport)
+ {
+ Vector2 strSize = new();
+ foreach (Rune r in str)
+ {
+ if (font.TryGetCharMetrics(r, 1, out var metrics))
+ {
+ strSize.X += metrics.Width;
+ strSize.Y = Math.Max(strSize.Y, metrics.Height);
+ }
+ }
+
+ Vector2 pos = new Vector2((viewport.X - strSize.X) / 2, strSize.Y + 110);
+ return pos;
+ }
+
+ private Vector2 CalcPositionDescription(Font font, string str, Vector2 viewport)
+ {
+ Vector2 strSize = new();
+ foreach (Rune r in str)
+ {
+ if (font.TryGetCharMetrics(r, 1, out var metrics))
+ {
+ strSize.X += metrics.Width;
+ strSize.Y = Math.Max(strSize.Y, metrics.Height);
+ }
+ }
+
+ Vector2 pos = new Vector2((viewport.X - strSize.X) / 2, strSize.Y + 110 + 140); //140 should be enough to give the title font space
+ return pos;
+ }
+}
diff --git a/Content.Client/_Mono/CombatMusic/ClientCombatMusicSystem.cs b/Content.Client/_Mono/CombatMusic/ClientCombatMusicSystem.cs
deleted file mode 100644
index 118e91b2c41..00000000000
--- a/Content.Client/_Mono/CombatMusic/ClientCombatMusicSystem.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using Content.Client.Audio;
-using Content.Shared._Mono.CCVar;
-using Content.Shared._Mono.CombatMusic;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Components;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Configuration;
-using Robust.Shared.Player;
-
-namespace Content.Client._Mono.CombatMusic;
-
-public sealed class ClientCombatMusicSystem : EntitySystem
-{
- [Dependency] private readonly IConfigurationManager _cfg = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly ContentAudioSystem _contentAudio = default!;
-
- private bool _enabled = true;
- private EntityUid? _stream;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeNetworkEvent(OnStart);
- SubscribeNetworkEvent(OnStop);
- Subs.CVar(_cfg, MonoCVars.CombatMusicEnabled, OnCVarChanged, true);
- }
-
- public override void Shutdown()
- {
- base.Shutdown();
- StopPlayback();
- }
-
- private void OnCVarChanged(bool enabled)
- {
- _enabled = enabled;
- if (!_enabled)
- StopPlayback();
- }
-
- private void StopPlayback()
- {
- if (_stream != null)
- {
- _audio.Stop(_stream);
- _stream = null;
- }
- }
-
- private void OnStart(CombatMusicStartEvent ev)
- {
- if (!_enabled)
- return;
-
- StopPlayback();
-
- var spec = new SoundPathSpecifier(ev.SoundPath);
- var parms = AudioParams.Default.WithVolume(ev.VolumeDb).WithLoop(ev.Loop);
- var stream = _audio.PlayGlobal(spec, Filter.Local(), false, parms);
- _stream = stream?.Entity;
- }
-
- private void OnStop(CombatMusicStopEvent ev)
- {
- if (_stream != null && ev.FadeOutDuration > 0f && TryComp(_stream, out AudioComponent? component))
- {
- _contentAudio.FadeOut(_stream, component, ev.FadeOutDuration);
- _stream = null;
- }
- else
- {
- StopPlayback();
- }
- }
-}
-
-
diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs
index 3276cb6589e..5aced4c0a69 100644
--- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs
+++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs
@@ -39,6 +39,8 @@
using Content.Server.Station.Systems; // Frontier
using Content.Server.Shuttles.Systems;
using Content.Server._NF.Salvage.Expeditions.Structure; // Frontier
+using Robust.Shared.GameObjects;
+using Content.Shared._Crescent.SpaceBiomes;
namespace Content.Server.Salvage;
diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
index c87833f0da7..c1e220e803a 100644
--- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
+++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
@@ -32,6 +32,10 @@
using Robust.Shared.Player;
using Robust.Shared.Utility;
using FTLMapComponent = Content.Shared.Shuttles.Components.FTLMapComponent;
+using Content.Server.Salvage.Expeditions;
+using Content.Shared._Mono.Ships;
+using Content.Shared._Crescent.SpaceBiomes;
+using Robust.Shared.Prototypes;
namespace Content.Server.Shuttles.Systems;
@@ -98,6 +102,8 @@ public sealed partial class ShuttleSystem
private EntityQuery _immuneQuery;
private EntityQuery _statusQuery;
+ [Dependency] private readonly IEntityManager _entManager = default!; // Mono
+
private void InitializeFTL()
{
SubscribeLocalEvent(OnStationPostInit);
diff --git a/Content.Server/Station/StationConfig.cs b/Content.Server/Station/StationConfig.cs
index 8bc0caf1aad..aded34894ab 100644
--- a/Content.Server/Station/StationConfig.cs
+++ b/Content.Server/Station/StationConfig.cs
@@ -15,5 +15,10 @@ public sealed partial class StationConfig
[DataField("components", required: true)]
public ComponentRegistry StationComponentOverrides = default!;
+
+ // Crescent - used to add components to grid. rn used for music & biome sys
+ [DataField]
+ public ComponentRegistry gridComponents = new();
+ // Crescent
}
diff --git a/Content.Server/Station/Systems/StationSystem.cs b/Content.Server/Station/Systems/StationSystem.cs
index 456d3c6b802..0622996d20a 100644
--- a/Content.Server/Station/Systems/StationSystem.cs
+++ b/Content.Server/Station/Systems/StationSystem.cs
@@ -354,6 +354,10 @@ public EntityUid InitializeNewStation(StationConfig stationConfig, IEnumerable())
{
AddGridToStation(station, grid, null, data, name);
+ // Crescent - used to add components directly to a grid from yaml
+ foreach (var (_, component) in stationConfig.gridComponents)
+ EntityManager.AddComponent(grid, component, true);
+ // Crescent
}
var ev = new StationPostInitEvent((station, data));
diff --git a/Content.Server/_Mono/CombatMusic/CombatMusicComponent.cs b/Content.Server/_Mono/CombatMusic/CombatMusicComponent.cs
deleted file mode 100644
index 080173919ed..00000000000
--- a/Content.Server/_Mono/CombatMusic/CombatMusicComponent.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using Robust.Shared.Audio;
-
-namespace Content.Server._Mono.CombatMusic;
-
-///
-/// Component that tracks combat music state for a grid.
-///
-[RegisterComponent]
-public sealed partial class CombatMusicComponent : Component
-{
- ///
- /// The time when the last shot was fired from any gunnery control on this grid.
- ///
- [DataField]
- public TimeSpan LastFiringTime = TimeSpan.Zero;
-
- ///
- /// Whether combat music is currently playing.
- ///
- [DataField]
- public bool MusicPlaying;
-
- ///
- /// The audio stream entity for the combat music.
- ///
- [ViewVariables]
- public EntityUid? MusicStream = null;
-
- ///
- /// The sounds to play for combat music. One will be randomly selected each time music starts.
- ///
- [DataField]
- public List CombatMusicSounds = new()
- {
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/forced_to_the_fringes.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/dubmood_cluster_1.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/field_directive.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/voyager_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/smiling_abyss.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/shell_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/ancient_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/augmented_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/corporate_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/morph_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/distant_lights_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/crystal_battle_remastered.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/insurgence_battle.ogg"),
- new SoundPathSpecifier("/Audio/_Mono/CombatMusic/galactic_battle.ogg"),
- };
-
- ///
- /// How long to wait after the last shot before stopping the music (in seconds).
- ///
- [DataField]
- public float MusicTimeout = 15f;
-
- ///
- /// The volume of the combat music.
- ///
- [DataField]
- public float Volume = -5f;
-
- ///
- /// How long to fade out the music before stopping (in seconds).
- ///
- [DataField]
- public float FadeOutDuration = 5f;
-
- ///
- /// Whether the fade-out has already been initiated.
- ///
- public bool FadeInitiated;
-}
-
diff --git a/Content.Server/_Mono/CombatMusic/CombatMusicSystem.cs b/Content.Server/_Mono/CombatMusic/CombatMusicSystem.cs
deleted file mode 100644
index 2b4b2aafa1c..00000000000
--- a/Content.Server/_Mono/CombatMusic/CombatMusicSystem.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using Content.Shared._Mono.CombatMusic;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Random;
-using Robust.Shared.Timing;
-
-namespace Content.Server._Mono.CombatMusic;
-
-///
-/// System that manages combat music playback when gunnery control fires.
-///
-public sealed class CombatMusicSystem : EntitySystem
-{
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IRobustRandom _robustRandom = default!;
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- var curTime = _timing.CurTime;
- var query = AllEntityQuery();
-
- while (query.MoveNext(out var gridUid, out var comp))
- {
- if (!comp.MusicPlaying)
- continue;
-
- var timeSinceLastShot = curTime - comp.LastFiringTime;
- var remainingTime = comp.MusicTimeout - timeSinceLastShot.TotalSeconds;
-
- if (!comp.FadeInitiated && remainingTime <= comp.FadeOutDuration && remainingTime > 0)
- {
- comp.FadeInitiated = true;
- var filter = Filter.Empty().AddInGrid(gridUid, EntityManager);
- RaiseNetworkEvent(new CombatMusicStopEvent(comp.FadeOutDuration), filter);
- }
-
- if (timeSinceLastShot.TotalSeconds >= comp.MusicTimeout)
- {
- StopCombatMusic(gridUid, comp);
- }
- }
- }
-
- ///
- /// Triggers combat music for a grid, starting it if not already playing
- ///
- public void TriggerCombatMusic(EntityUid gridUid)
- {
- var comp = EnsureComp(gridUid);
-
- comp.LastFiringTime = _timing.CurTime;
-
- if (!comp.MusicPlaying)
- {
- StartCombatMusic(gridUid, comp);
- }
- }
-
- ///
- /// Starts playing combat music for all players on the grid.
- ///
- private void StartCombatMusic(EntityUid gridUid, CombatMusicComponent comp)
- {
- if (comp.CombatMusicSounds.Count == 0)
- {
- Logger.Warning($"CombatMusicComponent on {gridUid} has no sounds configured!");
- return;
- }
-
- var selectedSound = comp.CombatMusicSounds[_robustRandom.Next(0, comp.CombatMusicSounds.Count)];
-
- var filter = Filter.Empty().AddInGrid(gridUid, EntityManager);
-
- var path = ((SoundPathSpecifier) selectedSound).Path.ToString();
- RaiseNetworkEvent(new CombatMusicStartEvent(path, comp.Volume, true), filter);
-
- comp.MusicPlaying = true;
- comp.FadeInitiated = false;
- }
-
- ///
- /// Stops combat music on the grid.
- ///
- private void StopCombatMusic(EntityUid gridUid, CombatMusicComponent comp)
- {
- if (comp.MusicStream != null && Exists(comp.MusicStream.Value))
- {
- Del(comp.MusicStream.Value);
- }
-
- comp.MusicStream = null;
- comp.MusicPlaying = false;
-
- if (!comp.FadeInitiated)
- {
- RaiseNetworkEvent(new CombatMusicStopEvent());
- }
-
- RemComp(gridUid);
- }
-}
-
diff --git a/Content.Server/_Mono/FireControl/FireControlSystem.cs b/Content.Server/_Mono/FireControl/FireControlSystem.cs
index a8c974040d1..763277e9aad 100644
--- a/Content.Server/_Mono/FireControl/FireControlSystem.cs
+++ b/Content.Server/_Mono/FireControl/FireControlSystem.cs
@@ -10,7 +10,6 @@
using System.Linq;
using Content.Shared.Physics;
using System.Numerics;
-using Content.Server._Mono.CombatMusic;
using Content.Server._Mono.SpaceArtillery;
using Content.Server._Mono.SpaceArtillery.Components;
using Content.Server.Power.EntitySystems;
@@ -31,8 +30,6 @@ public sealed partial class FireControlSystem : EntitySystem
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly RotateToFaceSystem _rotateToFace = default!;
- [Dependency] private readonly CombatMusicSystem _combatMusic = default!;
-
///
/// Dictionary of entities that have visualization enabled
///
@@ -462,11 +459,6 @@ public void FireWeapons(EntityUid server, List weapons, NetCoordinate
artilleryFired = true;
}
}
-
- if (artilleryFired)
- {
- TriggerCombatMusic(server);
- }
}
///
@@ -766,18 +758,6 @@ public bool ToggleVisualization(EntityUid entityUid)
RaiseNetworkEvent(new FireControlVisualizationEvent(netEntity, directions));
return true;
}
-
- ///
- /// Triggers combat music for the grid that the console is on.
- ///
- private void TriggerCombatMusic(EntityUid consoleUid)
- {
- var gridUid = _xform.GetGrid(consoleUid);
- if (gridUid == null)
- return;
-
- _combatMusic.TriggerCombatMusic(gridUid.Value);
- }
}
public sealed class FireControllableStatusReportEvent : EntityEventArgs
diff --git a/Content.Shared/Audio/AmbientMusicPrototype.cs b/Content.Shared/Audio/AmbientMusicPrototype.cs
index f5a1d895bfb..db39d0fb164 100644
--- a/Content.Shared/Audio/AmbientMusicPrototype.cs
+++ b/Content.Shared/Audio/AmbientMusicPrototype.cs
@@ -1,5 +1,4 @@
using Content.Shared.Random;
-using Content.Shared.Random.Rules;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -12,29 +11,9 @@ namespace Content.Shared.Audio;
[Prototype]
public sealed partial class AmbientMusicPrototype : IPrototype
{
- [IdDataField] public string ID { get; private set; } = string.Empty;
-
- ///
- /// Traditionally you'd prioritise most rules to least as priority but in our case we'll just be explicit.
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("priority")]
- public int Priority = 0;
-
- ///
- /// Can we interrupt this ambience for a better prototype if possible?
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("interruptable")]
- public bool Interruptable = false;
-
- ///
- /// Do we fade-in. Useful for songs.
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("fadeIn")]
- public bool FadeIn;
+ [IdDataField] public string ID { get; } = string.Empty;
[ViewVariables(VVAccess.ReadWrite), DataField("sound", required: true)]
public SoundSpecifier Sound = default!;
- [ViewVariables(VVAccess.ReadWrite), DataField("rules", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))]
- public string Rules = string.Empty;
}
diff --git a/Content.Shared/CCVar/CCVars.Audio.cs b/Content.Shared/CCVar/CCVars.Audio.cs
index daf6d4bb70c..d0795476721 100644
--- a/Content.Shared/CCVar/CCVars.Audio.cs
+++ b/Content.Shared/CCVar/CCVars.Audio.cs
@@ -47,12 +47,23 @@ public sealed partial class CCVars
///
public static readonly CVarDef AmbientMusicVolume =
CVarDef.Create("ambience.music_volume", 1.5f, CVar.ARCHIVE | CVar.CLIENTONLY);
+ ///
+ /// Ambience music volume.
+ ///
+ public static readonly CVarDef CombatMusicVolume =
+ CVarDef.Create("ambience.combat_music_volume", 1.5f, CVar.ARCHIVE | CVar.CLIENTONLY);
- ///
- /// Lobby / round end music volume.
- ///
- public static readonly CVarDef LobbyMusicVolume =
- CVarDef.Create("ambience.lobby_music_volume", 0.50f, CVar.ARCHIVE | CVar.CLIENTONLY);
+ ///
+ /// Ambience music volume.
+ ///
+ public static readonly CVarDef CombatMusicEnabled =
+ CVarDef.Create("ambience.combat_music_enabled", true, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+ ///
+ /// Lobby / round end music volume.
+ ///
+ public static readonly CVarDef LobbyMusicVolume =
+ CVarDef.Create("ambience.lobby_music_volume", 0.50f, CVar.ARCHIVE | CVar.CLIENTONLY);
///
/// UI volume.
diff --git a/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs b/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs
index a032b621ead..f2e873276f3 100644
--- a/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs
+++ b/Content.Shared/NPC/Components/NpcFactionMemberComponent.cs
@@ -6,13 +6,13 @@
namespace Content.Shared.NPC.Components;
-[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem))]
+[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem)), AutoGenerateComponentState] //Mono - autogeneratecomponentstate to replicate factions 2 clients
public sealed partial class NpcFactionMemberComponent : Component
{
///
/// Factions this entity is a part of.
///
- [DataField]
+ [DataField, AutoNetworkedField] // Mono - needed for clientside music system to know which music to play
public HashSet> Factions = new();
///
diff --git a/Content.Shared/Shuttles/Components/FTLMapComponent.cs b/Content.Shared/Shuttles/Components/FTLMapComponent.cs
index 4d57b0901f8..53380609c07 100644
--- a/Content.Shared/Shuttles/Components/FTLMapComponent.cs
+++ b/Content.Shared/Shuttles/Components/FTLMapComponent.cs
@@ -1,4 +1,6 @@
+using Content.Shared._Crescent.SpaceBiomes;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
namespace Content.Shared.Shuttles.Components;
@@ -25,4 +27,10 @@ public sealed partial class FTLMapComponent : Component
///
[DataField, AutoNetworkedField]
public bool Beacons;
+
+ ///
+ /// Mono: used for ContentAudioSystem.AmbientMusic.cs & SpaceBiomeSystem.cs to communicate biome on FTL
+ ///
+ [DataField]
+ public ProtoId Biome = "BiomeFTL";
}
diff --git a/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomePrototype.cs b/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomePrototype.cs
new file mode 100644
index 00000000000..7efb39431ff
--- /dev/null
+++ b/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomePrototype.cs
@@ -0,0 +1,16 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Crescent.SpaceBiomes;
+
+[Prototype("ambientSpaceBiome")]
+public sealed class SpaceBiomePrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ [DataField(required: true)]
+ public string Name = "";
+
+ [DataField(required: false)]
+ public string Description = "";
+}
diff --git a/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomeSourceComponent.cs b/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomeSourceComponent.cs
new file mode 100644
index 00000000000..ed156036fc6
--- /dev/null
+++ b/Content.Shared/_Crescent/SpaceBiomes/SpaceBiomeSourceComponent.cs
@@ -0,0 +1,29 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared._Crescent.SpaceBiomes;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class SpaceBiomeSourceComponent : Component
+{
+ [AutoNetworkedField]
+ [DataField(required: true)]
+ public ProtoId Id;
+
+ ///
+ /// Distance at which swap should begin
+ /// null = infinite distance
+ ///
+ [AutoNetworkedField]
+ [DataField(required: true)]
+ public float? SwapDistance;
+
+
+ ///
+ /// If multiple biomes are overlapping, biome with the highest priority is applied
+ ///
+ [AutoNetworkedField]
+ [DataField]
+ public float Priority;
+}
diff --git a/Content.Shared/_Crescent/Vessel/VesselInfoComponent.cs b/Content.Shared/_Crescent/Vessel/VesselInfoComponent.cs
new file mode 100644
index 00000000000..93b1cda7eea
--- /dev/null
+++ b/Content.Shared/_Crescent/Vessel/VesselInfoComponent.cs
@@ -0,0 +1,13 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Crescent.Vessel;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+public sealed partial class VesselInfoComponent : Component
+{
+ ///
+ /// exists to give the client the vessel's name. used for SpaceBiomeSystem to be fully clientside.
+ ///
+ [DataField, AutoNetworkedField]
+ public string Description = "A metal coffin.";
+}
diff --git a/Content.Shared/_Crescent/Vessel/VesselMusicComponent.cs b/Content.Shared/_Crescent/Vessel/VesselMusicComponent.cs
new file mode 100644
index 00000000000..f0670831ac6
--- /dev/null
+++ b/Content.Shared/_Crescent/Vessel/VesselMusicComponent.cs
@@ -0,0 +1,12 @@
+using Content.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Crescent.Vessel;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+public sealed partial class VesselMusicComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public ProtoId AmbientMusicPrototype;
+}
diff --git a/Content.Shared/_Mono/CCVar/CCVars.Mono.cs b/Content.Shared/_Mono/CCVar/CCVars.Mono.cs
index 381bb9845e1..65550e2c18a 100644
--- a/Content.Shared/_Mono/CCVar/CCVars.Mono.cs
+++ b/Content.Shared/_Mono/CCVar/CCVars.Mono.cs
@@ -81,11 +81,30 @@ public sealed partial class MonoCVars
#region Audio
///
- /// Whether the client should hear combat music triggered by ship artillery.
+ /// HULLROT: Wether or not to play combat music when combatmode is on.
///
public static readonly CVarDef CombatMusicEnabled =
CVarDef.Create("mono.combat_music.enabled", true, CVar.ARCHIVE | CVar.CLIENTONLY);
+ ///
+ /// HULLROT: Combat mode music volume.
+ ///
+ public static readonly CVarDef CombatMusicVolume =
+ CVarDef.Create("mono.combat_music_volume", 1.5f, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+ ///
+ /// HULLROT: Time needed with combatmode on to turn on combat music.
+ ///
+ public static readonly CVarDef CombatMusicWindUpTime =
+ CVarDef.Create("mono.combat_music_windup_time", 3, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+ ///
+ /// HULLROT: Time needed with combatmode off to turn off combat music.
+ ///
+ public static readonly CVarDef CombatMusicWindDownTime =
+ CVarDef.Create("mono.combat_music_winddown_time", 30, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+
///
/// Whether to render sounds with echo when they are in 'large' open, rooved areas.
///
diff --git a/Content.Shared/_Mono/CombatMusic/CombatMusicEvent.cs b/Content.Shared/_Mono/CombatMusic/CombatMusicEvent.cs
deleted file mode 100644
index d10da25aa98..00000000000
--- a/Content.Shared/_Mono/CombatMusic/CombatMusicEvent.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Robust.Shared.Serialization;
-
-namespace Content.Shared._Mono.CombatMusic;
-
-///
-/// Sent by the server to clients to start combat music locally.
-///
-[Serializable, NetSerializable]
-public sealed class CombatMusicStartEvent : EntityEventArgs
-{
- public string SoundPath { get; }
- public float VolumeDb { get; }
- public bool Loop { get; }
-
- public CombatMusicStartEvent(string soundPath, float volumeDb, bool loop)
- {
- SoundPath = soundPath;
- VolumeDb = volumeDb;
- Loop = loop;
- }
-}
-
-///
-/// Sent by the server to clients to stop combat music locally.
-///
-[Serializable, NetSerializable]
-public sealed class CombatMusicStopEvent : EntityEventArgs
-{
- ///
- /// How long to fade out the music before stopping.
- ///
- public float FadeOutDuration { get; }
-
- public CombatMusicStopEvent(float fadeOutDuration = 0f)
- {
- FadeOutDuration = fadeOutDuration;
- }
-}
-
-
diff --git a/Resources/Audio/_Crescent/Ambient/Expedition/expedition_1.ogg b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_1.ogg
new file mode 100644
index 00000000000..1a47dbb9973
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/Expedition/expedition_2.ogg b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_2.ogg
new file mode 100644
index 00000000000..650a3539189
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/Expedition/expedition_3.ogg b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_3.ogg
new file mode 100644
index 00000000000..7c37681c153
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/Expedition/expedition_3.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/FTL/ftl_1.ogg b/Resources/Audio/_Crescent/Ambient/FTL/ftl_1.ogg
new file mode 100644
index 00000000000..0bdbe28a75a
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/FTL/ftl_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_i.ogg b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_i.ogg
new file mode 100644
index 00000000000..96da13fd715
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_i.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_ii.ogg b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_ii.ogg
new file mode 100644
index 00000000000..73b31349083
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_ii.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_iii.ogg b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_iii.ogg
new file mode 100644
index 00000000000..10fe1ffc61b
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/FarReaches/far_reaches_iii.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg
new file mode 100644
index 00000000000..60853fb02f8
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_2.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_2.ogg
new file mode 100644
index 00000000000..4c1748cb513
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_3.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_3.ogg
new file mode 100644
index 00000000000..7889980dfe7
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_3.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_4.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_4.ogg
new file mode 100644
index 00000000000..7f8647a1a1c
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_4.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_5.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_5.ogg
new file mode 100644
index 00000000000..518f6e6bf0c
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_5.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_6.ogg b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_6.ogg
new file mode 100644
index 00000000000..a628b0b5899
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/InnerRing/inner_ring_6.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_1.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_1.ogg
new file mode 100644
index 00000000000..866c7e388bf
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_2.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_2.ogg
new file mode 100644
index 00000000000..f8c094f4e2c
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_3.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_3.ogg
new file mode 100644
index 00000000000..3c6fd648f6d
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_3.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_4.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_4.ogg
new file mode 100644
index 00000000000..d06c059b622
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_4.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_5.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_5.ogg
new file mode 100644
index 00000000000..05037166017
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_5.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_6.ogg b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_6.ogg
new file mode 100644
index 00000000000..9fa7e1a67a7
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/MiddleRing/middle_ring_6.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_1.ogg b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_1.ogg
new file mode 100644
index 00000000000..794b67d920c
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_2.ogg b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_2.ogg
new file mode 100644
index 00000000000..dda8df47622
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_3.ogg b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_3.ogg
new file mode 100644
index 00000000000..6cfadff8c8c
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_3.ogg differ
diff --git a/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_4.ogg b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_4.ogg
new file mode 100644
index 00000000000..6c5c9577557
Binary files /dev/null and b/Resources/Audio/_Crescent/Ambient/OuterRing/outer_ring_4.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/PDV_1.ogg b/Resources/Audio/_Crescent/CombatMode/PDV_1.ogg
new file mode 100644
index 00000000000..964214b9589
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/PDV_1.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/PDV_2.ogg b/Resources/Audio/_Crescent/CombatMode/PDV_2.ogg
new file mode 100644
index 00000000000..46b2bccce14
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/PDV_2.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/TSFMC_1.ogg b/Resources/Audio/_Crescent/CombatMode/TSFMC_1.ogg
new file mode 100644
index 00000000000..906ace8ca07
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/TSFMC_1.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/TSFMC_2.ogg b/Resources/Audio/_Crescent/CombatMode/TSFMC_2.ogg
new file mode 100644
index 00000000000..c913df8abf9
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/TSFMC_2.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/default_1.ogg b/Resources/Audio/_Crescent/CombatMode/default_1.ogg
new file mode 100644
index 00000000000..ca8b6d06e99
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/default_1.ogg differ
diff --git a/Resources/Audio/_Crescent/CombatMode/default_2.ogg b/Resources/Audio/_Crescent/CombatMode/default_2.ogg
new file mode 100644
index 00000000000..ed42283138b
Binary files /dev/null and b/Resources/Audio/_Crescent/CombatMode/default_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Camelot/camelot_1.ogg b/Resources/Audio/_Crescent/Stations/Camelot/camelot_1.ogg
new file mode 100644
index 00000000000..c446812124f
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Camelot/camelot_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Camelot/camelot_2.ogg b/Resources/Audio/_Crescent/Stations/Camelot/camelot_2.ogg
new file mode 100644
index 00000000000..b9bb50337ee
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Camelot/camelot_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_1.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_1.ogg
new file mode 100644
index 00000000000..acc64d65921
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_1.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_2.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_2.ogg
new file mode 100644
index 00000000000..5e701b9722e
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_2.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_3.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_3.ogg
new file mode 100644
index 00000000000..93427dafa33
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_3.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_4.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_4.ogg
new file mode 100644
index 00000000000..4c2ecb5147d
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_4.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_5.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_5.ogg
new file mode 100644
index 00000000000..338575dc3a8
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_5.ogg differ
diff --git a/Resources/Audio/_Crescent/Stations/Colossus/colossus_6.ogg b/Resources/Audio/_Crescent/Stations/Colossus/colossus_6.ogg
new file mode 100644
index 00000000000..6548416ed6e
Binary files /dev/null and b/Resources/Audio/_Crescent/Stations/Colossus/colossus_6.ogg differ
diff --git a/Resources/Audio/_Crescent/attributions.txt b/Resources/Audio/_Crescent/attributions.txt
new file mode 100644
index 00000000000..e5b163c9002
--- /dev/null
+++ b/Resources/Audio/_Crescent/attributions.txt
@@ -0,0 +1,52 @@
+all kevin macleod falls under this \/\/
+Licensed under Creative Commons: By Attribution 4.0
+https://creativecommons.org/licenses/by/4.0/
+all kevin macleoud falls under this /\/\
+
+/Ambient/InnerRing
+inner_ring_1 - [FTL Multiverse OST] Plains Explore
+inner_ring_2 - Paul Ruskay - Kharak System / Homeworld Soundtrack
+inner_ring_3 - Homeworld 2 Soundtrack 13 - Sarum
+inner_ring_4 - [FTL Multiverse OST] Morph Explore
+inner_ring_5 - [FTL Multiverse OST] Serenity Explore
+inner_ring_6 - [FTL Multiverse OST] Dynasty Explore
+/Ambient/MiddleRing
+middle_ring_1 - Shadowlands 2 Bridge Kevin MacLeod (incompetech.com)
+middle_ring_2 - Shadowlands 4 Breath Kevin MacLeod (incompetech.com)
+middle_ring_3 - Shadowlands 5 Antechamber Kevin MacLeod (incompetech.com)
+middle_ring_4 - Homeworld 2 Sountrack 05 - Tanis
+middle_ring_5 - Homeworld 2 Sountrack 21 - Gehenna
+middle_ring_6 - Paul Ruskay - Ship Graveyard / Homeworld Soundtrack
+/Ambient/OuterRing
+outer_ring_1 - Shadowlands 6 The Pit Kevin MacLeod (incompetech.com)
+outer_ring_2 - Shadowlands 7 Codex Kevin MacLeod (incompetech.com)
+outer_ring_3 - Paul Ruskay - Diamond Shoals / Homeworld Sountrack
+outer_ring_4 - Paul Ruskay - Great Wastelands / Homeworld Sountrack
+/Ambient/FarReaches
+far_reaches_i - System Shock 2, Rickenbacker Pod 1
+far_reaches_ii - System Shock 2, Rickenbacker Pod 2
+far_reaches_iii - System Shock 2, Rickenbacker Pod Bridge
+/Ambient/FTL
+ftl_1 - [FTL Multiverse OST] Shining Explore
+/Ambient/Expedition
+expedition_1 - System Shock 2, Command 3
+expedition_2 - System Shock 2, Medsci 2
+expedition_3 - System Shock 2, Recreation
+
+/CombatMode
+default_1 - System Shock 2, Command 2
+default_2 - System Shock 2, Hydroponics 1
+TSFMC_1 - Deus Ex - 026 - Levedev's Airfield - Combat
+TSFMC_2 - Deus Ex - 041 - Hong Kong Helipad - Combat
+PDV_1 - Homeworld 2 Soundtrack - Battle 01
+PDV_2 - Homeworld 2 Soundtrack - Battle 04
+/Stations/Colossus
+colossus_1 - Deus Ex - Invisible War - Seattle - Heron's Loft
+colossus_2 - Deus Ex - Invisible War - Seattle - Minister of Culture's Penthouse
+colossus_3 - Deus Ex - Invisible War - Seattle - Mr. O'Rourke's Apartment
+colossus_4 - Deus Ex - Invisible War - Seattle - Tarsus Recreation
+colossus_5 - Deus Ex - Invisible War - Seattle - Upper Seattle City Centre
+colossus_6 - Deus Ex - Invisible War - Trier - Nine Worlds Tavern
+/Stations/Camelot
+camelot_1 - Deus Ex - Invisible War - Liberty Island - Aquinas Hub
+camelot_2 - Deus Ex - Invisible War - Liberty Island - UNATCO Ruins
\ No newline at end of file
diff --git a/Resources/Audio/_Mono/CombatMusic/ancient_battle.ogg b/Resources/Audio/_Mono/CombatMusic/ancient_battle.ogg
deleted file mode 100644
index e5a77889973..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/ancient_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/augmented_battle.ogg b/Resources/Audio/_Mono/CombatMusic/augmented_battle.ogg
deleted file mode 100644
index e1398103f5c..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/augmented_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/corporate_battle.ogg b/Resources/Audio/_Mono/CombatMusic/corporate_battle.ogg
deleted file mode 100644
index c511fdf1554..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/corporate_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/credits.txt b/Resources/Audio/_Mono/CombatMusic/credits.txt
deleted file mode 100644
index e34c21af730..00000000000
--- a/Resources/Audio/_Mono/CombatMusic/credits.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-FTL Multiverse OST - https://www.youtube.com/playlist?list=PLXARrpodicQsHXiZoecojFMsoXvrTN1Hv
-Forced To The Fringes - https://www.youtube.com/watch?v=BVvt-CrC2-w
-Field Directive - https://www.youtube.com/watch?v=1CanbLql0I0
-Dubmood - Cluster 1 - https://www.youtube.com/watch?v=0qsCemVoi3s
\ No newline at end of file
diff --git a/Resources/Audio/_Mono/CombatMusic/crystal_battle_remastered.ogg b/Resources/Audio/_Mono/CombatMusic/crystal_battle_remastered.ogg
deleted file mode 100644
index 68f04b3e53b..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/crystal_battle_remastered.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/distant_lights_battle.ogg b/Resources/Audio/_Mono/CombatMusic/distant_lights_battle.ogg
deleted file mode 100644
index f8060f5499c..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/distant_lights_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/dubmood_cluster_1.ogg b/Resources/Audio/_Mono/CombatMusic/dubmood_cluster_1.ogg
deleted file mode 100644
index 9b5537e10d2..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/dubmood_cluster_1.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/field_directive.ogg b/Resources/Audio/_Mono/CombatMusic/field_directive.ogg
deleted file mode 100644
index 98787b03937..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/field_directive.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/forced_to_the_fringes.ogg b/Resources/Audio/_Mono/CombatMusic/forced_to_the_fringes.ogg
deleted file mode 100644
index 5a775f96fda..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/forced_to_the_fringes.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/galactic_battle.ogg b/Resources/Audio/_Mono/CombatMusic/galactic_battle.ogg
deleted file mode 100644
index 36c5ab016e3..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/galactic_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/insurgence_battle.ogg b/Resources/Audio/_Mono/CombatMusic/insurgence_battle.ogg
deleted file mode 100644
index 12b415b3f3d..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/insurgence_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/morph_battle.ogg b/Resources/Audio/_Mono/CombatMusic/morph_battle.ogg
deleted file mode 100644
index d0d56796886..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/morph_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/shell_battle.ogg b/Resources/Audio/_Mono/CombatMusic/shell_battle.ogg
deleted file mode 100644
index e0401009dba..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/shell_battle.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/smiling_abyss.ogg b/Resources/Audio/_Mono/CombatMusic/smiling_abyss.ogg
deleted file mode 100644
index 3aa39b4334d..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/smiling_abyss.ogg and /dev/null differ
diff --git a/Resources/Audio/_Mono/CombatMusic/voyager_battle.ogg b/Resources/Audio/_Mono/CombatMusic/voyager_battle.ogg
deleted file mode 100644
index eef3c71842c..00000000000
Binary files a/Resources/Audio/_Mono/CombatMusic/voyager_battle.ogg and /dev/null differ
diff --git a/Resources/Fonts/Doloto/Doloto-Regular.ttf b/Resources/Fonts/Doloto/Doloto-Regular.ttf
new file mode 100644
index 00000000000..21d6cd30710
Binary files /dev/null and b/Resources/Fonts/Doloto/Doloto-Regular.ttf differ
diff --git a/Resources/Fonts/Doloto/LICENSE.txt b/Resources/Fonts/Doloto/LICENSE.txt
new file mode 100644
index 00000000000..c16216028d6
--- /dev/null
+++ b/Resources/Fonts/Doloto/LICENSE.txt
@@ -0,0 +1,70 @@
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+
+ "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+ "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+ "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+ "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+ "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+ "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+ "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
+ "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
+
+ to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
+ to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
+ to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
+ to Distribute and Publicly Perform Adaptations.
+
+ For the avoidance of doubt:
+ Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
+ Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
+ Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
+
+The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+ You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
+ If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
+ Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+ Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+
+8. Miscellaneous
+
+ Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+ If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+ No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
+ This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+ The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
+
+
+ Creative Commons Notice
+
+ Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+
+ Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+
+ Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/Resources/Fonts/Helvetica/Helvetica-Bold.ttf b/Resources/Fonts/Helvetica/Helvetica-Bold.ttf
new file mode 100644
index 00000000000..332b66ca7e8
Binary files /dev/null and b/Resources/Fonts/Helvetica/Helvetica-Bold.ttf differ
diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
index 1867c5206ca..1187d3be79f 100644
--- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
+++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
@@ -36,6 +36,10 @@ ui-options-exped-music-volume = Expedition music volume:
ui-options-lobby-volume = Lobby & Round-end volume:
ui-options-interface-volume = Interface volume:
ui-options-ambience-max-sounds = Ambience simultaneous sounds:
+ui-options-combat-music-volume = Combat Music volume:
+ui-options-combat-music-windup = Combat Music activation time:
+ui-options-combat-music-winddown = Combat Music deactivation time:
+ui-options-combat-music-sec-format = { $value } sec
ui-options-lobby-music = Lobby & Round-end Music
ui-options-restart-sounds = Round Restart Sounds
ui-options-event-music = Event Music
diff --git a/Resources/Locale/ru-RU/_Lua/escape-menu/ui/options-menu.ftl b/Resources/Locale/ru-RU/_Lua/escape-menu/ui/options-menu.ftl
index 0f35ff22572..02377fddae3 100644
--- a/Resources/Locale/ru-RU/_Lua/escape-menu/ui/options-menu.ftl
+++ b/Resources/Locale/ru-RU/_Lua/escape-menu/ui/options-menu.ftl
@@ -1,4 +1,9 @@
-ui-options-general-area-echo = Эхо.
-ui-options-area-echo-enabled = Применить эхо в больших открытых пространствах.
-ui-options-area-echo-highres = Если включено использует улучшенные, но более медленные вычисления для эхо-сигналов.
-ui-options-combat-music = Музыка во время боя.
\ No newline at end of file
+ui-options-general-area-echo = Эхо
+ui-options-area-echo-enabled = Применить эхо в больших открытых пространствах
+ui-options-area-echo-highres = Если включено использует улучшенные, но более медленные вычисления для эхо-сигналов
+ui-options-combat-music = Музыка во время боя
+
+ui-options-combat-music-volume = Громкость музыки во время боя:
+ui-options-combat-music-windup = время до активаций музыки во время боя:
+ui-options-combat-music-winddown = время до деактиваций музыки во время боя:
+ui-options-combat-music-sec-format = { $value } сек
diff --git a/Resources/Prototypes/_Crescent/Biomes/biome_sources.yml b/Resources/Prototypes/_Crescent/Biomes/biome_sources.yml
new file mode 100644
index 00000000000..6dcf2ead349
--- /dev/null
+++ b/Resources/Prototypes/_Crescent/Biomes/biome_sources.yml
@@ -0,0 +1,100 @@
+# these are what actually decides a biome's spread.
+# as of 8/1/2026, these have all been placed on top of the station anchor on Colossus Central.
+# TODO: make an exped prototype & autoplace it on expeditions with a higher priority than 2k
+# 0-5000: inner ring
+# 5000-9500: middle ring
+# 9500-18000: outer ring
+# 18000+: uncharted space (fallback)
+# THESE MUST HAVE GLOBAL PVS OR THE CLIENT WON'T SEE THEM!!!!!!!!!!!!
+
+- type: entity
+ id: BaseBiomeSource
+ name: base biome source
+ components:
+ - type: GlobalPvs #needed so the client can see these past PVS range
+
+- type: entity
+ id: BiomeSourceFallback
+ parent: BaseBiomeSource
+ name: fallback biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeDefault
+ swapDistance: null #infinite
+ priority: 1
+
+
+# hardcoded in SalvageSystem.Expeditions.cs
+# components:
+# - type: SpaceBiomeSource
+# id: expedition_biome
+# swapDistance: null # infinite
+# priority: 2500 #should not matter, but big enough to override all the rest
+
+# hardcoded in ShuttleSystem.FasterThanLight.cs
+# components:
+# - type: SpaceBiomeSource
+# id: ftl_biome
+# swapDistance: null # infinite
+# priority: 2500 #should not matter, but big enough to override all the rest
+
+
+- type: entity
+ id: BiomeSourceFarReachesIII
+ parent: BaseBiomeSource
+ name: far reaches III biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeFarReachesIII
+ swapDistance: 28000 #beyond this is fallback
+ priority: 250
+
+- type: entity
+ id: BiomeSourceFarReachesII
+ parent: BaseBiomeSource
+ name: far reaches II biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeFarReachesII
+ swapDistance: 25000
+ priority: 500
+
+- type: entity
+ id: BiomeSourceFarReachesI
+ parent: BaseBiomeSource
+ name: far reaches I biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeFarReachesI
+ swapDistance: 22000
+ priority: 750
+
+- type: entity
+ id: BiomeSourceOuterRing
+ parent: BaseBiomeSource
+ name: outer ring biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeOuterRing
+ swapDistance: 18000
+ priority: 1000
+
+- type: entity
+ id: BiomeSourceMiddleRing
+ parent: BaseBiomeSource
+ name: middle ring biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeMiddleRing
+ swapDistance: 10000
+ priority: 1500
+
+- type: entity
+ id: BiomeSourceInnerRing
+ parent: BaseBiomeSource
+ name: inner ring biome source
+ components:
+ - type: SpaceBiomeSource
+ id: BiomeInnerRing
+ swapDistance: 5000
+ priority: 2000
diff --git a/Resources/Prototypes/_Crescent/Biomes/space_biomes.yml b/Resources/Prototypes/_Crescent/Biomes/space_biomes.yml
new file mode 100644
index 00000000000..d3f858af760
--- /dev/null
+++ b/Resources/Prototypes/_Crescent/Biomes/space_biomes.yml
@@ -0,0 +1,49 @@
+#fallback, don't remove
+- type: ambientSpaceBiome
+ id: BiomeDefault
+ name: Uncharted Space
+
+#expedition biome - hardcoded in SpawnSalvageMissionJob.cs
+- type: ambientSpaceBiome
+ id: BiomeExpedition
+ name: Expedition
+ description: "Uncharted land."
+
+#ftl biome - hardcoded in ShuttleSystem.FasterThanLight.cs
+- type: ambientSpaceBiome
+ id: BiomeFTL
+ name: Hyperspace
+ description: "Upon the pale horse, with Hell behind you."
+
+#far reaches
+
+- type: ambientSpaceBiome
+ id: BiomeFarReachesIII
+ name: Far Reaches III
+ description: "Things have learnt to walk that ought to crawl."
+
+- type: ambientSpaceBiome
+ id: BiomeFarReachesII
+ name: Far Reaches II
+ description: "There's nothing this far out anymore. Or that's what you would find yourself wishing."
+
+- type: ambientSpaceBiome
+ id: BiomeFarReachesI
+ name: Far Reaches I
+ description: "You feel like the asteroids are moving even while you aren't. Or watching you. Or both."
+
+#actual biomes
+- type: ambientSpaceBiome
+ id: BiomeOuterRing
+ name: Outer Ring
+ description: "Far from radio range. Far from help. The stars here are dimmer."
+
+- type: ambientSpaceBiome
+ id: BiomeMiddleRing
+ name: Middle Ring
+ description: "Stellar outskirts, often crawling with riches and danger."
+
+- type: ambientSpaceBiome
+ id: BiomeInnerRing
+ name: Inner Ring
+ description: "Civilised space. The constellations seen from here are burned into your memory."
diff --git a/Resources/Prototypes/_Crescent/Music/ambient_music.yml b/Resources/Prototypes/_Crescent/Music/ambient_music.yml
new file mode 100644
index 00000000000..e7a3f4291f3
--- /dev/null
+++ b/Resources/Prototypes/_Crescent/Music/ambient_music.yml
@@ -0,0 +1,231 @@
+##
+## PLEASE READ BEFORE CONTRIBUTING! also, for clarity, check ContentAudioSystem.AmbientMusic.cs. i added comments!
+## for biome IDs, check space_biomes.yml
+##
+## for your ambient music to be valid, you need two prototypes
+## first, an ambientMusic prototype. the id !!!!MUST MATCH THE BIOME ID!!!! that you are making ambient music for, and it must have a sound collection
+## otherwise it will play the fallback track.
+## second, a soundCollection prototype. its id can be different, but please keep it the same as the biome for consistency
+## the music selected from the sound collection will be chosen randomly whenever the appropriate music is required to be played
+##
+## combat music default/fallback is called "CombatMusicDefault"
+## factional combat music must be called "CombatMusic"
+## this is because the way combat music is fetched is ContentAudioSystem.AmbientMusic.cs filtering for NPCFactionComponent and turning them into faction IDs,
+## then combining them with CombatMusic to get CombatMusicPDV.
+##
+## valid factions:
+## TSFMC
+## PDV (PirateNF in factions until i fucking fix this)
+## (everyone else gets combatmodedefault)
+##
+## ALSO!! UPDATE attributions.txt OR I WILL FIND YOU!!!!!!!!!!!!
+## @.2, 2025/6/23
+
+
+#example
+- type: ambientMusic
+ id: test
+ sound:
+ collection: test
+
+- type: soundCollection
+ id: test
+ files:
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg
+
+#fallback, don't remove, but can change songs
+
+- type: ambientMusic
+ id: BiomeDefault
+ sound:
+ collection: BiomeDefault
+
+- type: soundCollection
+ id: BiomeDefault
+ files:
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg
+
+# HARDCODED - FTL
+- type: ambientMusic
+ id: BiomeFTL
+ sound:
+ collection: BiomeFTL
+
+- type: soundCollection
+ id: BiomeFTL
+ files:
+ - /Audio/_Crescent/Ambient/FTL/ftl_1.ogg
+
+# HARDCODED - EXPEDITION
+- type: ambientMusic
+ id: BiomeExpedition
+ sound:
+ collection: BiomeExpedition
+
+- type: soundCollection
+ id: BiomeExpedition
+ files:
+ - /Audio/_Crescent/Ambient/Expedition/expedition_1.ogg
+ - /Audio/_Crescent/Ambient/Expedition/expedition_2.ogg
+ - /Audio/_Crescent/Ambient/Expedition/expedition_3.ogg
+
+#combat music for anyone who isn't PDV or TFSMC (for now)
+
+- type: ambientMusic
+ id: CombatModeDefault
+ sound:
+ collection: CombatModeDefault
+
+- type: soundCollection
+ id: CombatModeDefault
+ files:
+ - /Audio/_Crescent/CombatMode/default_1.ogg
+ - /Audio/_Crescent/CombatMode/default_2.ogg
+
+## FACTION COMBAT MUSIC TRACKS
+## ID MUST BE "combatmode"!
+
+### PDV
+- type: ambientMusic
+ id: CombatModePDV
+ sound:
+ collection: CombatModePDV
+
+- type: soundCollection
+ id: CombatModePDV
+ files:
+ - /Audio/_Crescent/CombatMode/PDV_1.ogg
+ - /Audio/_Crescent/CombatMode/PDV_2.ogg
+
+### TSFMC
+- type: ambientMusic
+ id: CombatModeTSFMC
+ sound:
+ collection: CombatModeTSFMC
+
+- type: soundCollection
+ id: CombatModeTSFMC
+ files:
+ - /Audio/_Crescent/CombatMode/TSFMC_1.ogg
+ - /Audio/_Crescent/CombatMode/TSFMC_2.ogg
+
+
+### BIOME AMBIENT
+
+# far reaches III
+- type: ambientMusic
+ id: BiomeFarReachesIII
+ sound:
+ collection: BiomeFarReachesIII
+
+- type: soundCollection
+ id: BiomeFarReachesIII
+ files:
+ - /Audio/_Crescent/Ambient/FarReaches/far_reaches_iii.ogg
+
+# far reaches II
+- type: ambientMusic
+ id: BiomeFarReachesII
+ sound:
+ collection: BiomeFarReachesII
+
+- type: soundCollection
+ id: BiomeFarReachesII
+ files:
+ - /Audio/_Crescent/Ambient/FarReaches/far_reaches_ii.ogg
+
+# far reaches I
+- type: ambientMusic
+ id: BiomeFarReachesI
+ sound:
+ collection: BiomeFarReachesI
+
+- type: soundCollection
+ id: BiomeFarReachesI
+ files:
+ - /Audio/_Crescent/Ambient/FarReaches/far_reaches_i.ogg
+
+# outer ring
+
+- type: ambientMusic
+ id: BiomeOuterRing
+ sound:
+ collection: BiomeOuterRing
+
+- type: soundCollection
+ id: BiomeOuterRing
+ files:
+ - /Audio/_Crescent/Ambient/OuterRing/outer_ring_1.ogg
+ - /Audio/_Crescent/Ambient/OuterRing/outer_ring_2.ogg
+ - /Audio/_Crescent/Ambient/OuterRing/outer_ring_3.ogg
+ - /Audio/_Crescent/Ambient/OuterRing/outer_ring_4.ogg
+
+# middle ring
+
+- type: ambientMusic
+ id: BiomeMiddleRing
+ sound:
+ collection: BiomeMiddleRing
+
+- type: soundCollection
+ id: BiomeMiddleRing
+ files:
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_1.ogg
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_2.ogg
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_3.ogg
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_4.ogg
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_5.ogg
+ - /Audio/_Crescent/Ambient/MiddleRing/middle_ring_6.ogg
+
+# inner ring
+
+- type: ambientMusic
+ id: BiomeInnerRing
+ sound:
+ collection: BiomeInnerRing
+
+- type: soundCollection
+ id: BiomeInnerRing
+ files:
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_1.ogg
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_2.ogg
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_3.ogg
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_4.ogg
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_5.ogg
+ - /Audio/_Crescent/Ambient/InnerRing/inner_ring_6.ogg
+
+
+
+#STATIONS
+#EXPLANATION: shuttle/station prototypes have a VesselMusic component.
+#They point to an ambientMusicPrototype and that prototype MUST be here, otherwise it does nothing.
+#so for example, in balreska.yml, it's VesselMusic component points to PortBalreskaMusic, which is the first item here.
+
+#Camelot Station
+- type: ambientMusic
+ id: CamelotMusic
+ sound:
+ collection: CamelotMusic
+
+- type: soundCollection
+ id: CamelotMusic
+ files:
+ - /Audio/_Crescent/Stations/Camelot/camelot_1.ogg
+ - /Audio/_Crescent/Stations/Camelot/camelot_2.ogg
+
+#Colossus Central
+- type: ambientMusic
+ id: ColossusCentralMusic
+ sound:
+ collection: ColossusCentralMusic
+
+- type: soundCollection
+ id: ColossusCentralMusic
+ files:
+ - /Audio/_Crescent/Stations/Colossus/colossus_1.ogg
+ - /Audio/_Crescent/Stations/Colossus/colossus_2.ogg
+ - /Audio/_Crescent/Stations/Colossus/colossus_3.ogg
+ - /Audio/_Crescent/Stations/Colossus/colossus_4.ogg
+ - /Audio/_Crescent/Stations/Colossus/colossus_5.ogg
+ - /Audio/_Crescent/Stations/Colossus/colossus_6.ogg
+
diff --git a/Resources/Prototypes/_Mono/Shipyard/BlackMarket/europa.yml b/Resources/Prototypes/_Mono/Shipyard/BlackMarket/europa.yml
index efa3071391e..ae1bbe065c5 100644
--- a/Resources/Prototypes/_Mono/Shipyard/BlackMarket/europa.yml
+++ b/Resources/Prototypes/_Mono/Shipyard/BlackMarket/europa.yml
@@ -25,6 +25,9 @@
minPlayers: 0
stations:
Europa:
+ gridComponents:
+ - type: VesselInfo
+ description: "The Saturn's Sister ship."
stationProto: StandardFrontierVessel
components:
- type: StationNameSetup
diff --git a/Resources/Prototypes/_Mono/Shipyard/BlackMarket/vulture.yml b/Resources/Prototypes/_Mono/Shipyard/BlackMarket/vulture.yml
index ede223dfcba..bdb88594030 100644
--- a/Resources/Prototypes/_Mono/Shipyard/BlackMarket/vulture.yml
+++ b/Resources/Prototypes/_Mono/Shipyard/BlackMarket/vulture.yml
@@ -26,6 +26,9 @@
stations:
Vulture:
stationProto: StandardFrontierVessel
+ gridComponents:
+ - type: VesselInfo
+ description: "Its rumored that a Vulture razed a small planet on its own. Now its your turn to find out for yourself."
components:
- type: StationNameSetup
mapNameTemplate: 'Vulture {1}'
diff --git a/Resources/Prototypes/_Mono/Shipyard/USSP/tunguska.yml b/Resources/Prototypes/_Mono/Shipyard/USSP/tunguska.yml
index 652c6c2d226..f3c132c6e3d 100644
--- a/Resources/Prototypes/_Mono/Shipyard/USSP/tunguska.yml
+++ b/Resources/Prototypes/_Mono/Shipyard/USSP/tunguska.yml
@@ -24,6 +24,9 @@
stations:
Tunguska:
stationProto: StandardFrontierVessel
+ gridComponents:
+ - type: VesselInfo
+ description: "Outdated, yet still reliable."
components:
- type: StationNameSetup
mapNameTemplate: 'Tunguska {1}'
diff --git a/Resources/Prototypes/_NF/Maps/Outpost/frontier.yml b/Resources/Prototypes/_NF/Maps/Outpost/frontier.yml
index fff0266c146..bc90b87c6ae 100644
--- a/Resources/Prototypes/_NF/Maps/Outpost/frontier.yml
+++ b/Resources/Prototypes/_NF/Maps/Outpost/frontier.yml
@@ -9,6 +9,13 @@
stations:
Frontier:
stationProto: StandardFrontierStation
+ # Lua add start
+ gridComponents:
+ - type: VesselMusic
+ ambientMusicPrototype: ColossusCentralMusic
+ - type: VesselInfo
+ description: "Хотя бы здесь вы можете почувствовать себя в безопасности."
+ # Lua add end
components:
- type: FrontierParking # Lua
- type: RadarBlipIcon
diff --git a/Resources/Prototypes/audio.yml b/Resources/Prototypes/audio.yml
index a215ee45be1..e73d97fdc71 100644
--- a/Resources/Prototypes/audio.yml
+++ b/Resources/Prototypes/audio.yml
@@ -1,253 +1,256 @@
-- type: ambientMusic
- id: Morgue
- sound:
- params:
- volume: -12
- collection: AmbienceSpooky
- rules: NearMorgue
- priority: 4
-
-- type: ambientMusic
- id: SpookyFog
- sound:
- params:
- volume: -12
- collection: AmbienceSpookyFog
- rules: NearSpookyFog
- priority: 5
-
-- type: ambientMusic
- id: Holy
- sound:
- params:
- volume: -12
- collection: AmbienceHoly
- rules: NearPrayable
- priority: 4
-
-- type: ambientMusic
- id: Train
- sound:
- params:
- volume: -8
- collection: AmbienceTrain
- rules: NearTrain
- priority: 4
-
-# Departments
-- type: ambientMusic
- id: Medical
- sound:
- params:
- volume: -12
- collection: AmbienceMedical
- rules: NearMedical
- priority: 3
-
-- type: ambientMusic
- id: Engineering
- sound:
- params:
- volume: -12
- collection: AmbienceEngineering
- rules: NearEngineering
- priority: 3
-
-# General areas
-- type: ambientMusic
- id: Maintenance
- sound:
- params:
- volume: -12
- collection: AmbienceMaintenance
- rules: NearMaintenance
- priority: 2
-
-- type: ambientMusic
- id: Space
- sound:
- params:
- volume: -10
- collection: AmbienceSpace
- fadeIn: true
- interruptable: true
- rules: InSpace
- priority: 1
-
-- type: ambientMusic
- id: Mining
- sound:
- params:
- volume: -12
- collection: AmbienceMining
- rules: OnMapGrid
- fadeIn: true
- interruptable: true
- priority: 1
-
-## Fallback if nothing else found
-- type: ambientMusic
- id: General
- sound:
- params:
- volume: -12
- collection: AmbienceGeneral
- rules: AlwaysTrue
-
-# Sound collections
-- type: soundCollection
- id: AmbienceEngineering
- files:
- - /Audio/Ambience/ambiatmos.ogg
- - /Audio/Ambience/ambiatmos2.ogg
- - /Audio/Ambience/ambisin1.ogg
- - /Audio/Ambience/ambisin2.ogg
- - /Audio/Ambience/ambisin3.ogg
- - /Audio/Ambience/ambisin4.ogg
- - /Audio/Ambience/ambitech.ogg
- - /Audio/Ambience/ambitech2.ogg
- - /Audio/Ambience/ambitech3.ogg
-
-- type: soundCollection
- id: AmbienceGeneral
- files:
- - /Audio/Ambience/ambigen1.ogg
- - /Audio/Ambience/ambigen3.ogg
- - /Audio/Ambience/ambigen4.ogg
- - /Audio/Ambience/ambigen5.ogg
- - /Audio/Ambience/ambigen6.ogg
- - /Audio/Ambience/ambigen7.ogg
- - /Audio/Ambience/ambigen8.ogg
- - /Audio/Ambience/ambigen9.ogg
- - /Audio/Ambience/ambigen10.ogg
- - /Audio/Ambience/ambigen11.ogg
- - /Audio/Ambience/ambigen12.ogg
- - /Audio/Ambience/ambigen14.ogg
- - /Audio/Ambience/ambigen15.ogg
-
-- type: soundCollection
- id: AmbienceHoly
- files:
- - /Audio/Ambience/ambicha1.ogg
- - /Audio/Ambience/ambicha2.ogg
- - /Audio/Ambience/ambicha3.ogg
- - /Audio/Ambience/ambicha4.ogg
- - /Audio/Ambience/ambiholy.ogg
- - /Audio/Ambience/ambiholy2.ogg
- - /Audio/Ambience/ambiholy3.ogg
-
-- type: soundCollection
- id: AmbienceMaintenance
- files:
- - /Audio/Ambience/ambimaint1.ogg
- - /Audio/Ambience/ambimaint2.ogg
- - /Audio/Ambience/ambimaint3.ogg
- - /Audio/Ambience/ambimaint4.ogg
- - /Audio/Ambience/ambimaint5.ogg
- - /Audio/Ambience/ambitech2.ogg
- - /Audio/Voice/Misc/lowHiss1.ogg
- - /Audio/Voice/Misc/lowHiss2.ogg
- - /Audio/Voice/Misc/lowHiss3.ogg
- - /Audio/Voice/Misc/lowHiss4.ogg
- - /Audio/Ambience/maintambience.ogg
-
-- type: soundCollection
- id: AmbienceMedical
- files:
- - /Audio/Ambience/ambinice.ogg
-
-- type: soundCollection
- id: AmbienceMining
- files:
- - /Audio/Ambience/ambicave.ogg
- - /Audio/Ambience/ambidanger.ogg
- - /Audio/Ambience/ambidanger2.ogg
- - /Audio/Ambience/ambilava1.ogg
- - /Audio/Ambience/ambilava2.ogg
- - /Audio/Ambience/ambilava3.ogg
- - /Audio/Ambience/ambimaint1.ogg
- - /Audio/Ambience/ambimine.ogg
- - /Audio/Ambience/ambiruin.ogg
- - /Audio/Ambience/ambiruin2.ogg
- - /Audio/Ambience/ambiruin3.ogg
- - /Audio/Ambience/ambiruin4.ogg
- - /Audio/Ambience/ambiruin5.ogg
- - /Audio/Ambience/ambiruin6.ogg
- - /Audio/Ambience/ambiruin7.ogg
- - /Audio/Ambience/voyage_neverending.ogg
- - /Audio/Ambience/unanchored.ogg
-
-- type: soundCollection
- id: AmbienceRuins
- files:
- - /Audio/Ambience/ambicave.ogg
- - /Audio/Ambience/ambidanger.ogg
- - /Audio/Ambience/ambidanger2.ogg
- - /Audio/Ambience/ambimaint1.ogg
- - /Audio/Ambience/ambimine.ogg
- - /Audio/Ambience/ambimystery.ogg
- - /Audio/Ambience/ambiruin.ogg
- - /Audio/Ambience/ambiruin2.ogg
- - /Audio/Ambience/ambiruin3.ogg
- - /Audio/Ambience/ambiruin4.ogg
- - /Audio/Ambience/ambiruin5.ogg
- - /Audio/Ambience/ambiruin6.ogg
- - /Audio/Ambience/ambiruin7.ogg
-
-- type: soundCollection
- id: AmbienceSpace
- files:
- - /Audio/Ambience/constellations.ogg
- - /Audio/Ambience/starlight.ogg
- - /Audio/Ambience/drifting.ogg
- - /Audio/Ambience/spookyspace1.ogg
- - /Audio/Ambience/spookyspace2.ogg
- - /Audio/Ambience/ambispace2.ogg
- - /Audio/Lobby/title2.ogg
-
-- type: soundCollection
- id: AmbienceSpooky
- files:
- - /Audio/Ambience/ambimo1.ogg
- - /Audio/Ambience/ambimo2.ogg
- - /Audio/Ambience/ambimystery.ogg
- - /Audio/Ambience/ambiodd.ogg
- - /Audio/Ambience/ambiruin6.ogg
- - /Audio/Ambience/ambiruin7.ogg
-
-- type: soundCollection
- id: AmbienceTrain
- files:
- - /Audio/Ambience/ambitrain1.ogg
- - /Audio/Ambience/ambitrain2.ogg
- - /Audio/Ambience/ambitrain3.ogg
- - /Audio/Ambience/ambiruin3.ogg
- - /Audio/Ambience/ambiruin5.ogg
- - /Audio/Ambience/ambiruin6.ogg
-
-- type: soundCollection
- id: AmbienceSpookyFog
- files:
- - /Audio/Ambience/spookyspace1.ogg
- - /Audio/Ambience/spookyspace2.ogg
- - /Audio/Ambience/ambimo2.ogg
- - /Audio/Ambience/ambilava1.ogg
- - /Audio/Ambience/ambilava2.ogg
- - /Audio/Ambience/ambiruin2.ogg
- - /Audio/Ambience/ambiruin3.ogg
- - /Audio/Ambience/ambiruin4.ogg
- - /Audio/Ambience/ambiruin5.ogg
- - /Audio/Ambience/ambiruin6.ogg
- - /Audio/Ambience/ambiruin7.ogg
- - /Audio/Ambience/ambidanger.ogg
- - /Audio/Ambience/ambidanger2.ogg
- - /Audio/Ambience/ambimine.ogg
-
-## Background noise on station, separate to ambient music.
-- type: soundCollection
- id: AmbienceStation
- files:
- - /Audio/Ambience/shipambience.ogg
+# nuked cuz new ContentAudioSystem.AmbientMusic.cs
+# .2 - 8/1/2026
+
+# - type: ambientMusic
+# id: Morgue
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceSpooky
+# rules: NearMorgue
+# priority: 4
+
+# - type: ambientMusic
+# id: SpookyFog
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceSpookyFog
+# rules: NearSpookyFog
+# priority: 5
+
+# - type: ambientMusic
+# id: Holy
+# sound:
+# params:
+# volume: -8
+# collection: AmbienceHoly
+# rules: NearPrayable
+# priority: 4
+
+# - type: ambientMusic
+# id: Train
+# sound:
+# params:
+# volume: -4
+# collection: AmbienceTrain
+# rules: NearTrain
+# priority: 4
+
+# # Departments
+# - type: ambientMusic
+# id: Medical
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceMedical
+# rules: NearMedical
+# priority: 3
+
+# - type: ambientMusic
+# id: Engineering
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceEngineering
+# rules: NearEngineering
+# priority: 3
+
+# # General areas
+# - type: ambientMusic
+# id: Maintenance
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceMaintenance
+# rules: NearMaintenance
+# priority: 2
+
+# - type: ambientMusic
+# id: Space
+# sound:
+# params:
+# volume: -5
+# collection: AmbienceSpace
+# fadeIn: true
+# interruptable: true
+# rules: InSpace
+# priority: 1
+
+# - type: ambientMusic
+# id: Mining
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceMining
+# rules: OnMapGrid
+# fadeIn: true
+# interruptable: true
+# priority: 1
+
+# ## Fallback if nothing else found
+# - type: ambientMusic
+# id: General
+# sound:
+# params:
+# volume: -6
+# collection: AmbienceGeneral
+# rules: AlwaysTrue
+
+# # Sound collections
+# - type: soundCollection
+# id: AmbienceEngineering
+# files:
+# - /Audio/Ambience/ambiatmos.ogg
+# - /Audio/Ambience/ambiatmos2.ogg
+# - /Audio/Ambience/ambisin1.ogg
+# - /Audio/Ambience/ambisin2.ogg
+# - /Audio/Ambience/ambisin3.ogg
+# - /Audio/Ambience/ambisin4.ogg
+# - /Audio/Ambience/ambitech.ogg
+# - /Audio/Ambience/ambitech2.ogg
+# - /Audio/Ambience/ambitech3.ogg
+
+# - type: soundCollection
+# id: AmbienceGeneral
+# files:
+# - /Audio/Ambience/ambigen1.ogg
+# - /Audio/Ambience/ambigen3.ogg
+# - /Audio/Ambience/ambigen4.ogg
+# - /Audio/Ambience/ambigen5.ogg
+# - /Audio/Ambience/ambigen6.ogg
+# - /Audio/Ambience/ambigen7.ogg
+# - /Audio/Ambience/ambigen8.ogg
+# - /Audio/Ambience/ambigen9.ogg
+# - /Audio/Ambience/ambigen10.ogg
+# - /Audio/Ambience/ambigen11.ogg
+# - /Audio/Ambience/ambigen12.ogg
+# - /Audio/Ambience/ambigen14.ogg
+# - /Audio/Ambience/ambigen15.ogg
+
+# - type: soundCollection
+# id: AmbienceHoly
+# files:
+# - /Audio/Ambience/ambicha1.ogg
+# - /Audio/Ambience/ambicha2.ogg
+# - /Audio/Ambience/ambicha3.ogg
+# - /Audio/Ambience/ambicha4.ogg
+# - /Audio/Ambience/ambiholy.ogg
+# - /Audio/Ambience/ambiholy2.ogg
+# - /Audio/Ambience/ambiholy3.ogg
+
+# - type: soundCollection
+# id: AmbienceMaintenance
+# files:
+# - /Audio/Ambience/ambimaint1.ogg
+# - /Audio/Ambience/ambimaint2.ogg
+# - /Audio/Ambience/ambimaint3.ogg
+# - /Audio/Ambience/ambimaint4.ogg
+# - /Audio/Ambience/ambimaint5.ogg
+# - /Audio/Ambience/ambitech2.ogg
+# - /Audio/Voice/Misc/lowHiss1.ogg
+# - /Audio/Voice/Misc/lowHiss2.ogg
+# - /Audio/Voice/Misc/lowHiss3.ogg
+# - /Audio/Voice/Misc/lowHiss4.ogg
+# - /Audio/Ambience/maintambience.ogg
+# - /Audio/_Goobstation/Ambience/ambihullcreak.ogg # Goobstation
+
+# - type: soundCollection
+# id: AmbienceMedical
+# files:
+# - /Audio/Ambience/ambinice.ogg
+
+# - type: soundCollection
+# id: AmbienceMining
+# files:
+# - /Audio/Ambience/ambicave.ogg
+# - /Audio/Ambience/ambidanger.ogg
+# - /Audio/Ambience/ambidanger2.ogg
+# - /Audio/Ambience/ambilava1.ogg
+# - /Audio/Ambience/ambilava2.ogg
+# - /Audio/Ambience/ambilava3.ogg
+# - /Audio/Ambience/ambimaint1.ogg
+# - /Audio/Ambience/ambimine.ogg
+# - /Audio/Ambience/ambiruin.ogg
+# - /Audio/Ambience/ambiruin2.ogg
+# - /Audio/Ambience/ambiruin3.ogg
+# - /Audio/Ambience/ambiruin4.ogg
+# - /Audio/Ambience/ambiruin5.ogg
+# - /Audio/Ambience/ambiruin6.ogg
+# - /Audio/Ambience/ambiruin7.ogg
+# - /Audio/Ambience/voyage_neverending.ogg
+# - /Audio/Ambience/unanchored.ogg
+
+# - type: soundCollection
+# id: AmbienceRuins
+# files:
+# - /Audio/Ambience/ambicave.ogg
+# - /Audio/Ambience/ambidanger.ogg
+# - /Audio/Ambience/ambidanger2.ogg
+# - /Audio/Ambience/ambimaint1.ogg
+# - /Audio/Ambience/ambimine.ogg
+# - /Audio/Ambience/ambimystery.ogg
+# - /Audio/Ambience/ambiruin.ogg
+# - /Audio/Ambience/ambiruin2.ogg
+# - /Audio/Ambience/ambiruin3.ogg
+# - /Audio/Ambience/ambiruin4.ogg
+# - /Audio/Ambience/ambiruin5.ogg
+# - /Audio/Ambience/ambiruin6.ogg
+# - /Audio/Ambience/ambiruin7.ogg
+
+# - type: soundCollection
+# id: AmbienceSpace
+# files:
+# - /Audio/Ambience/constellations.ogg
+# - /Audio/Ambience/starlight.ogg
+# - /Audio/Ambience/drifting.ogg
+# - /Audio/Ambience/spookyspace1.ogg
+# - /Audio/Ambience/spookyspace2.ogg
+# - /Audio/Ambience/ambispace2.ogg
+
+# - type: soundCollection
+# id: AmbienceSpooky
+# files:
+# - /Audio/Ambience/ambimo1.ogg
+# - /Audio/Ambience/ambimo2.ogg
+# - /Audio/Ambience/ambimystery.ogg
+# - /Audio/Ambience/ambiodd.ogg
+# - /Audio/Ambience/ambiruin6.ogg
+# - /Audio/Ambience/ambiruin7.ogg
+
+# - type: soundCollection
+# id: AmbienceTrain
+# files:
+# - /Audio/Ambience/ambitrain1.ogg
+# - /Audio/Ambience/ambitrain2.ogg
+# - /Audio/Ambience/ambitrain3.ogg
+# - /Audio/Ambience/ambiruin3.ogg
+# - /Audio/Ambience/ambiruin5.ogg
+# - /Audio/Ambience/ambiruin6.ogg
+
+# - type: soundCollection
+# id: AmbienceSpookyFog
+# files:
+# - /Audio/Ambience/spookyspace1.ogg
+# - /Audio/Ambience/spookyspace2.ogg
+# - /Audio/Ambience/ambimo2.ogg
+# - /Audio/Ambience/ambilava1.ogg
+# - /Audio/Ambience/ambilava2.ogg
+# - /Audio/Ambience/ambiruin2.ogg
+# - /Audio/Ambience/ambiruin3.ogg
+# - /Audio/Ambience/ambiruin4.ogg
+# - /Audio/Ambience/ambiruin5.ogg
+# - /Audio/Ambience/ambiruin6.ogg
+# - /Audio/Ambience/ambiruin7.ogg
+# - /Audio/Ambience/ambidanger.ogg
+# - /Audio/Ambience/ambidanger2.ogg
+# - /Audio/Ambience/ambimine.ogg
+
+# ## Background noise on station, separate to ambient music.
+# - type: soundCollection
+# id: AmbienceStation
+# files:
+# - /Audio/Ambience/shipambience.ogg
# Rules
- type: rules
diff --git a/Resources/Prototypes/fonts.yml b/Resources/Prototypes/fonts.yml
index 03102cd341b..441acc574c2 100644
--- a/Resources/Prototypes/fonts.yml
+++ b/Resources/Prototypes/fonts.yml
@@ -45,3 +45,8 @@
- type: font
id: Emoji
path: /Fonts/NotoEmoji.ttf
+
+# Mono
+- type: font
+ id: Helvetica
+ path: /Fonts/Helvetica/Helvetica-Bold.ttf