Merge remote-tracking branch 'space-station-14/master' into 04-05-2024-upstream

# Conflicts:
#	Resources/Prototypes/Maps/Pools/default.yml
#	Resources/Prototypes/Maps/train.yml
This commit is contained in:
Ed
2024-05-04 21:13:30 +03:00
144 changed files with 1514 additions and 307 deletions

View File

@@ -96,6 +96,16 @@ public sealed class AlertLevelSystem : EntitySystem
RaiseLocalEvent(new AlertLevelPrototypeReloadedEvent());
}
public string GetLevel(EntityUid station, AlertLevelComponent? alert = null)
{
if (!Resolve(station, ref alert))
{
return string.Empty;
}
return alert.CurrentLevel;
}
public float GetAlertLevelDelay(EntityUid station, AlertLevelComponent? alert = null)
{
if (!Resolve(station, ref alert))

View File

@@ -12,9 +12,10 @@ namespace Content.Server.Atmos.Components
/// <summary>
/// Tool is functional only in allowed slots
/// </summary>
[DataField("allowedSlots")]
[DataField]
public SlotFlags AllowedSlots = SlotFlags.MASK | SlotFlags.HEAD;
public bool IsFunctional;
public EntityUid? ConnectedInternalsEntity;
}
}

View File

@@ -8,6 +8,7 @@ using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Content.Shared.Internals;
using Content.Shared.Inventory;
using Content.Shared.Roles;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Robust.Shared.Utility;
@@ -23,17 +24,29 @@ public sealed class InternalsSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
public const SlotFlags InventorySlots = SlotFlags.POCKET | SlotFlags.BELT;
private EntityQuery<InternalsComponent> _internalsQuery;
public override void Initialize()
{
base.Initialize();
_internalsQuery = GetEntityQuery<InternalsComponent>();
SubscribeLocalEvent<InternalsComponent, InhaleLocationEvent>(OnInhaleLocation);
SubscribeLocalEvent<InternalsComponent, ComponentStartup>(OnInternalsStartup);
SubscribeLocalEvent<InternalsComponent, ComponentShutdown>(OnInternalsShutdown);
SubscribeLocalEvent<InternalsComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
SubscribeLocalEvent<InternalsComponent, InternalsDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<StartingGearEquippedEvent>(OnStartingGear);
}
private void OnStartingGear(ref StartingGearEquippedEvent ev)
{
if (!_internalsQuery.TryComp(ev.Entity, out var internals) || internals.BreathToolEntity == null)
return;
ToggleInternals(ev.Entity, ev.Entity, force: false, internals);
}
private void OnGetInteractionVerbs(
@@ -217,7 +230,7 @@ public sealed class InternalsSystem : EntitySystem
if (component.BreathToolEntity is null || !AreInternalsWorking(component))
return 2;
// If pressure in the tank is below low pressure threshhold, flash warning on internals UI
// If pressure in the tank is below low pressure threshold, flash warning on internals UI
if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank)
&& gasTank.IsLowPressure)
{

View File

@@ -26,7 +26,7 @@ public abstract partial class GameRuleSystem<T> : EntitySystem where T : ICompon
SubscribeLocalEvent<T, GameRuleAddedEvent>(OnGameRuleAdded);
SubscribeLocalEvent<T, GameRuleStartedEvent>(OnGameRuleStarted);
SubscribeLocalEvent<T, GameRuleEndedEvent>(OnGameRuleEnded);
SubscribeLocalEvent<T, RoundEndTextAppendEvent>(OnRoundEndTextAppend);
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndTextAppend);
}
private void OnStartAttempt(RoundStartAttemptEvent args)
@@ -70,11 +70,16 @@ public abstract partial class GameRuleSystem<T> : EntitySystem where T : ICompon
Ended(uid, component, ruleData, args);
}
private void OnRoundEndTextAppend(Entity<T> ent, ref RoundEndTextAppendEvent args)
private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev)
{
if (!TryComp<GameRuleComponent>(ent, out var ruleData))
return;
AppendRoundEndText(ent, ent, ruleData, ref args);
var query = AllEntityQuery<T>();
while (query.MoveNext(out var uid, out var comp))
{
if (!TryComp<GameRuleComponent>(uid, out var ruleData))
continue;
AppendRoundEndText(uid, comp, ruleData, ref ev);
}
}
/// <summary>

View File

@@ -1,7 +1,7 @@
using Content.Server.Actions;
using Content.Server.Polymorph.Systems;
using Content.Shared.Zombies;
using Content.Server.Actions;
using Content.Server.Popups;
using Content.Shared.Actions;
using Content.Shared.Geras;
using Robust.Shared.Player;
@@ -10,8 +10,9 @@ namespace Content.Server.Geras;
/// <inheritdoc/>
public sealed class GerasSystem : SharedGerasSystem
{
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
[Dependency] private readonly PolymorphSystem _polymorphSystem = default!;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
/// <inheritdoc/>
@@ -19,6 +20,12 @@ public sealed class GerasSystem : SharedGerasSystem
{
SubscribeLocalEvent<GerasComponent, MorphIntoGeras>(OnMorphIntoGeras);
SubscribeLocalEvent<GerasComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GerasComponent, EntityZombifiedEvent>(OnZombification);
}
private void OnZombification(EntityUid uid, GerasComponent component, EntityZombifiedEvent args)
{
_actionsSystem.RemoveAction(uid, component.GerasActionEntity);
}
private void OnMapInit(EntityUid uid, GerasComponent component, MapInitEvent args)
@@ -29,6 +36,9 @@ public sealed class GerasSystem : SharedGerasSystem
private void OnMorphIntoGeras(EntityUid uid, GerasComponent component, MorphIntoGeras args)
{
if (HasComp<ZombieComponent>(uid))
return; // i hate zomber.
var ent = _polymorphSystem.PolymorphEntity(uid, component.GerasPolymorphId);
if (!ent.HasValue)
@@ -36,6 +46,7 @@ public sealed class GerasSystem : SharedGerasSystem
_popupSystem.PopupEntity(Loc.GetString("geras-popup-morph-message-others", ("entity", ent.Value)), ent.Value, Filter.PvsExcept(ent.Value), true);
_popupSystem.PopupEntity(Loc.GetString("geras-popup-morph-message-user"), ent.Value, ent.Value);
args.Handled = true;
}
}

View File

@@ -32,5 +32,11 @@ namespace Content.Server.Speech.Components
{
[DataField("accent", customTypeSerializer: typeof(PrototypeIdSerializer<ReplacementAccentPrototype>), required: true)]
public string Accent = default!;
/// <summary>
/// Allows you to substitute words, not always, but with some chance
/// </summary>
[DataField]
public float ReplacementChance = 1f;
}
}

View File

@@ -24,6 +24,9 @@ namespace Content.Server.Speech.EntitySystems
private void OnAccent(EntityUid uid, ReplacementAccentComponent component, AccentGetEvent args)
{
if (!_random.Prob(component.ReplacementChance))
return;
args.Message = ApplyReplacements(args.Message, component.Accent);
}
@@ -46,6 +49,12 @@ namespace Content.Server.Speech.EntitySystems
if (prototype.WordReplacements == null)
return message;
// Prohibition of repeated word replacements.
// All replaced words placed in the final message are placed here as dashes (___) with the same length.
// The regex search goes through this buffer message, from which the already replaced words are crossed out,
// ensuring that the replaced words cannot be replaced again.
var maskMessage = message;
foreach (var (first, replace) in prototype.WordReplacements)
{
var f = _loc.GetString(first);
@@ -53,10 +62,10 @@ namespace Content.Server.Speech.EntitySystems
// this is kind of slow but its not that bad
// essentially: go over all matches, try to match capitalization where possible, then replace
// rather than using regex.replace
for (int i = Regex.Count(message, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase); i > 0; i--)
for (int i = Regex.Count(maskMessage, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase); i > 0; i--)
{
// fetch the match again as the character indices may have changed
Match match = Regex.Match(message, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase);
Match match = Regex.Match(maskMessage, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase);
var replacement = r;
// Intelligently replace capitalization
@@ -80,6 +89,8 @@ namespace Content.Server.Speech.EntitySystems
// In-place replace the match with the transformed capitalization replacement
message = message.Remove(match.Index, match.Length).Insert(match.Index, replacement);
string mask = new string('_', match.Length);
maskMessage = message.Remove(match.Index, match.Length).Insert(match.Index, mask);
}
}

View File

@@ -59,6 +59,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
Subs.CVar(_configurationManager, CCVars.ICRandomCharacters, e => _randomizeCharacters = e, true);
_spawnerCallbacks = new Dictionary<SpawnPriorityPreference, Action<PlayerSpawningEvent>>()
@@ -181,7 +182,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
if (prototype?.StartingGear != null)
{
var startingGear = _prototypeManager.Index<StartingGearPrototype>(prototype.StartingGear);
EquipStartingGear(entity.Value, startingGear);
EquipStartingGear(entity.Value, startingGear, raiseEvent: false);
}
// Run loadouts after so stuff like storage loadouts can get
@@ -217,11 +218,14 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
}
// Handle any extra data here.
EquipStartingGear(entity.Value, startingGear);
EquipStartingGear(entity.Value, startingGear, raiseEvent: false);
}
}
}
var gearEquippedEv = new StartingGearEquippedEvent(entity.Value);
RaiseLocalEvent(entity.Value, ref gearEquippedEv, true);
if (profile != null)
{
if (prototype != null)

View File

@@ -0,0 +1,15 @@
using Content.Server.StationEvents.Events;
using Content.Server.AlertLevel;
using Robust.Shared.Prototypes;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(AlertLevelInterceptionRule))]
public sealed partial class AlertLevelInterceptionRuleComponent : Component
{
/// <summary>
/// Alert level to set the station to when the event starts.
/// </summary>
[DataField]
public string AlertLevel = "blue";
}

View File

@@ -0,0 +1,23 @@
using Content.Server.GameTicking.Components;
using Content.Server.StationEvents.Components;
using Content.Server.AlertLevel;
namespace Content.Server.StationEvents.Events;
public sealed class AlertLevelInterceptionRule : StationEventSystem<AlertLevelInterceptionRuleComponent>
{
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
protected override void Started(EntityUid uid, AlertLevelInterceptionRuleComponent component, GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
if (!TryGetRandomStation(out var chosenStation))
return;
if (_alertLevelSystem.GetLevel(chosenStation.Value) != "green")
return;
_alertLevelSystem.SetLevel(chosenStation.Value, component.AlertLevel, true, true, true);
}
}