diff --git a/Content.Benchmarks/ComponentQueryBenchmark.cs b/Content.Benchmarks/ComponentQueryBenchmark.cs new file mode 100644 index 0000000000..11c7ab9d5f --- /dev/null +++ b/Content.Benchmarks/ComponentQueryBenchmark.cs @@ -0,0 +1,273 @@ +#nullable enable +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using Content.IntegrationTests; +using Content.IntegrationTests.Pair; +using Content.Shared.Clothing.Components; +using Content.Shared.Doors.Components; +using Content.Shared.Item; +using Robust.Server.GameObjects; +using Robust.Shared; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Random; + +namespace Content.Benchmarks; + +/// +/// Benchmarks for comparing the speed of various component fetching/lookup related methods, including directed event +/// subscriptions +/// +[Virtual] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +[CategoriesColumn] +public class ComponentQueryBenchmark +{ + public const string Map = "Maps/atlas.yml"; + + private TestPair _pair = default!; + private IEntityManager _entMan = default!; + private MapId _mapId = new(10); + private EntityQuery _itemQuery; + private EntityQuery _clothingQuery; + private EntityQuery _mapQuery; + private EntityUid[] _items = default!; + + [GlobalSetup] + public void Setup() + { + ProgramShared.PathOffset = "../../../../"; + PoolManager.Startup(typeof(QueryBenchSystem).Assembly); + + _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _entMan = _pair.Server.ResolveDependency(); + + _itemQuery = _entMan.GetEntityQuery(); + _clothingQuery = _entMan.GetEntityQuery(); + _mapQuery = _entMan.GetEntityQuery(); + + _pair.Server.ResolveDependency().SetSeed(42); + _pair.Server.WaitPost(() => + { + var success = _entMan.System().TryLoad(_mapId, Map, out _); + if (!success) + throw new Exception("Map load failed"); + _pair.Server.MapMan.DoMapInitialize(_mapId); + }).GetAwaiter().GetResult(); + + _items = new EntityUid[_entMan.Count()]; + var i = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out var uid, out _)) + { + _items[i++] = uid; + } + } + + [GlobalCleanup] + public async Task Cleanup() + { + await _pair.DisposeAsync(); + PoolManager.Shutdown(); + } + + #region TryComp + + /// + /// Baseline TryComp benchmark. When the benchmark was created, around 40% of the items were clothing. + /// + [Benchmark(Baseline = true)] + [BenchmarkCategory("TryComp")] + public int TryComp() + { + var hashCode = 0; + foreach (var uid in _items) + { + if (_clothingQuery.TryGetComponent(uid, out var clothing)) + hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); + } + return hashCode; + } + + /// + /// Variant of that is meant to always fail to get a component. + /// + [Benchmark] + [BenchmarkCategory("TryComp")] + public int TryCompFail() + { + var hashCode = 0; + foreach (var uid in _items) + { + if (_mapQuery.TryGetComponent(uid, out var map)) + hashCode = HashCode.Combine(hashCode, map.GetHashCode()); + } + return hashCode; + } + + /// + /// Variant of that is meant to always succeed getting a component. + /// + [Benchmark] + [BenchmarkCategory("TryComp")] + public int TryCompSucceed() + { + var hashCode = 0; + foreach (var uid in _items) + { + if (_itemQuery.TryGetComponent(uid, out var item)) + hashCode = HashCode.Combine(hashCode, item.GetHashCode()); + } + return hashCode; + } + + /// + /// Variant of that uses `Resolve()` to try get the component. + /// + [Benchmark] + [BenchmarkCategory("TryComp")] + public int Resolve() + { + var hashCode = 0; + foreach (var uid in _items) + { + DoResolve(uid, ref hashCode); + } + return hashCode; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DoResolve(EntityUid uid, ref int hash, ClothingComponent? clothing = null) + { + if (_clothingQuery.Resolve(uid, ref clothing, false)) + hash = HashCode.Combine(hash, clothing.GetHashCode()); + } + + #endregion + + #region Enumeration + + [Benchmark] + [BenchmarkCategory("Item Enumerator")] + public int SingleItemEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out var item)) + { + hashCode = HashCode.Combine(hashCode, item.GetHashCode()); + } + + return hashCode; + } + + [Benchmark] + [BenchmarkCategory("Item Enumerator")] + public int DoubleItemEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out _, out var item)) + { + hashCode = HashCode.Combine(hashCode, item.GetHashCode()); + } + + return hashCode; + } + + [Benchmark] + [BenchmarkCategory("Item Enumerator")] + public int TripleItemEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out _, out _, out var xform)) + { + hashCode = HashCode.Combine(hashCode, xform.GetHashCode()); + } + + return hashCode; + } + + [Benchmark] + [BenchmarkCategory("Airlock Enumerator")] + public int SingleAirlockEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out var airlock)) + { + hashCode = HashCode.Combine(hashCode, airlock.GetHashCode()); + } + + return hashCode; + } + + [Benchmark] + [BenchmarkCategory("Airlock Enumerator")] + public int DoubleAirlockEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out _, out var door)) + { + hashCode = HashCode.Combine(hashCode, door.GetHashCode()); + } + + return hashCode; + } + + [Benchmark] + [BenchmarkCategory("Airlock Enumerator")] + public int TripleAirlockEnumerator() + { + var hashCode = 0; + var enumerator = _entMan.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out _, out _, out var xform)) + { + hashCode = HashCode.Combine(hashCode, xform.GetHashCode()); + } + + return hashCode; + } + + #endregion + + [Benchmark(Baseline = true)] + [BenchmarkCategory("Events")] + public int StructEvents() + { + var ev = new QueryBenchEvent(); + foreach (var uid in _items) + { + _entMan.EventBus.RaiseLocalEvent(uid, ref ev); + } + + return ev.HashCode; + } +} + +[ByRefEvent] +public struct QueryBenchEvent +{ + public int HashCode; +} + +public sealed class QueryBenchSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEvent); + } + + private void OnEvent(EntityUid uid, ClothingComponent component, ref QueryBenchEvent args) + { + args.HashCode = HashCode.Combine(args.HashCode, component.GetHashCode()); + } +} diff --git a/Content.Benchmarks/EntityQueryBenchmark.cs b/Content.Benchmarks/EntityQueryBenchmark.cs deleted file mode 100644 index cef6a5e35c..0000000000 --- a/Content.Benchmarks/EntityQueryBenchmark.cs +++ /dev/null @@ -1,137 +0,0 @@ -#nullable enable -using System; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; -using Content.IntegrationTests; -using Content.IntegrationTests.Pair; -using Content.Shared.Clothing.Components; -using Content.Shared.Item; -using Robust.Server.GameObjects; -using Robust.Shared; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Random; - -namespace Content.Benchmarks; - -[Virtual] -public class EntityQueryBenchmark -{ - public const string Map = "Maps/atlas.yml"; - - private TestPair _pair = default!; - private IEntityManager _entMan = default!; - private MapId _mapId = new MapId(10); - private EntityQuery _clothingQuery; - - [GlobalSetup] - public void Setup() - { - ProgramShared.PathOffset = "../../../../"; - PoolManager.Startup(null); - - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); - _entMan = _pair.Server.ResolveDependency(); - - _pair.Server.ResolveDependency().SetSeed(42); - _pair.Server.WaitPost(() => - { - var success = _entMan.System().TryLoad(_mapId, Map, out _); - if (!success) - throw new Exception("Map load failed"); - _pair.Server.MapMan.DoMapInitialize(_mapId); - }).GetAwaiter().GetResult(); - - _clothingQuery = _entMan.GetEntityQuery(); - - // Apparently ~40% of entities are items, and 1 in 6 of those are clothing. - /* - var entCount = _entMan.EntityCount; - var itemCount = _entMan.Count(); - var clothingCount = _entMan.Count(); - var itemRatio = (float) itemCount / entCount; - var clothingRatio = (float) clothingCount / entCount; - Console.WriteLine($"Entities: {entCount}. Items: {itemRatio:P2}. Clothing: {clothingRatio:P2}."); - */ - } - - [GlobalCleanup] - public async Task Cleanup() - { - await _pair.DisposeAsync(); - PoolManager.Shutdown(); - } - - [Benchmark] - public int HasComponent() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_entMan.HasComponent(uid)) - hashCode = HashCode.Combine(hashCode, uid.Id); - } - - return hashCode; - } - - [Benchmark] - public int HasComponentQuery() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_clothingQuery.HasComponent(uid)) - hashCode = HashCode.Combine(hashCode, uid.Id); - } - - return hashCode; - } - - [Benchmark] - public int TryGetComponent() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_entMan.TryGetComponent(uid, out ClothingComponent? clothing)) - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } - - [Benchmark] - public int TryGetComponentQuery() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out var _)) - { - if (_clothingQuery.TryGetComponent(uid, out var clothing)) - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } - - /// - /// Enumerate all entities with both an item and clothing component. - /// - [Benchmark] - public int Enumerator() - { - var hashCode = 0; - var enumerator = _entMan.AllEntityQueryEnumerator(); - while (enumerator.MoveNext(out var _, out var clothing)) - { - hashCode = HashCode.Combine(hashCode, clothing.GetHashCode()); - } - - return hashCode; - } -} diff --git a/Content.Benchmarks/MapLoadBenchmark.cs b/Content.Benchmarks/MapLoadBenchmark.cs index 261e164f17..a3319e3067 100644 --- a/Content.Benchmarks/MapLoadBenchmark.cs +++ b/Content.Benchmarks/MapLoadBenchmark.cs @@ -26,7 +26,7 @@ public class MapLoadBenchmark public void Setup() { ProgramShared.PathOffset = "../../../../"; - PoolManager.Startup(null); + PoolManager.Startup(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); var server = _pair.Server; diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs index c7f22bdb0c..0b4dd90762 100644 --- a/Content.Benchmarks/PvsBenchmark.cs +++ b/Content.Benchmarks/PvsBenchmark.cs @@ -49,7 +49,7 @@ public class PvsBenchmark #if !DEBUG ProgramShared.PathOffset = "../../../../"; #endif - PoolManager.Startup(null); + PoolManager.Startup(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); _entMan = _pair.Server.ResolveDependency(); diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index 8512107b69..0638d945aa 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -32,7 +32,7 @@ public class SpawnEquipDeleteBenchmark public async Task SetupAsync() { ProgramShared.PathOffset = "../../../../"; - PoolManager.Startup(null); + PoolManager.Startup(); _pair = await PoolManager.GetServerClient(); var server = _pair.Server; diff --git a/Content.Client/Gravity/GravitySystem.Shake.cs b/Content.Client/Gravity/GravitySystem.Shake.cs index c4356588d3..9b9918ca3e 100644 --- a/Content.Client/Gravity/GravitySystem.Shake.cs +++ b/Content.Client/Gravity/GravitySystem.Shake.cs @@ -25,7 +25,7 @@ public sealed partial class GravitySystem { var localPlayer = _playerManager.LocalEntity; - if (!TryComp(localPlayer, out var xform) || + if (!TryComp(localPlayer, out TransformComponent? xform) || xform.GridUid != uid && xform.MapUid != uid) { return; @@ -46,7 +46,7 @@ public sealed partial class GravitySystem var localPlayer = _playerManager.LocalEntity; - if (!TryComp(localPlayer, out var xform)) + if (!TryComp(localPlayer, out TransformComponent? xform)) return; if (xform.GridUid != uid || diff --git a/Content.Client/Maps/GridDraggingSystem.cs b/Content.Client/Maps/GridDraggingSystem.cs index 16357c8983..e82786847e 100644 --- a/Content.Client/Maps/GridDraggingSystem.cs +++ b/Content.Client/Maps/GridDraggingSystem.cs @@ -61,7 +61,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem { if (_dragging == null) return; - if (_lastMousePosition != null && TryComp(_dragging.Value, out var xform) && + if (_lastMousePosition != null && TryComp(_dragging.Value, out TransformComponent? xform) && TryComp(_dragging.Value, out var body) && xform.MapID == _lastMousePosition.Value.MapId) { @@ -104,7 +104,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem StartDragging(gridUid, Transform(gridUid).InvWorldMatrix.Transform(mousePos.Position)); } - if (!TryComp(_dragging, out var xform)) + if (!TryComp(_dragging, out TransformComponent? xform)) { StopDragging(); return; diff --git a/Content.Client/Salvage/SalvageSystem.cs b/Content.Client/Salvage/SalvageSystem.cs index fb305c5fdc..e1bce367ca 100644 --- a/Content.Client/Salvage/SalvageSystem.cs +++ b/Content.Client/Salvage/SalvageSystem.cs @@ -38,7 +38,7 @@ public sealed class SalvageSystem : SharedSalvageSystem var player = _playerManager.LocalEntity; - if (!TryComp(player, out var xform) || + if (!TryComp(player, out TransformComponent? xform) || !TryComp(xform.MapUid, out var expedition) || expedition.Stage < ExpeditionStage.MusicCountdown) { diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index d9584b60a6..676a6e583d 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -45,7 +45,7 @@ public sealed class SpriteFadeSystem : EntitySystem var spriteQuery = GetEntityQuery(); var change = ChangeRate * frameTime; - if (TryComp(player, out var playerXform) && + if (TryComp(player, out TransformComponent? playerXform) && _stateManager.CurrentState is GameplayState state && spriteQuery.TryGetComponent(player, out var playerSprite)) { diff --git a/Content.Client/Weapons/Misc/TetherGunSystem.cs b/Content.Client/Weapons/Misc/TetherGunSystem.cs index 634dbd24e7..398aeabb83 100644 --- a/Content.Client/Weapons/Misc/TetherGunSystem.cs +++ b/Content.Client/Weapons/Misc/TetherGunSystem.cs @@ -82,7 +82,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem const float BufferDistance = 0.1f; - if (TryComp(gun.TetherEntity, out var tetherXform) && + if (TryComp(gun.TetherEntity, out TransformComponent? tetherXform) && tetherXform.Coordinates.TryDistance(EntityManager, TransformSystem, coords, out var distance) && distance < BufferDistance) { diff --git a/Content.IntegrationTests/PoolManager.Prototypes.cs b/Content.IntegrationTests/PoolManager.Prototypes.cs index 760e8b1d37..eb7518ea15 100644 --- a/Content.IntegrationTests/PoolManager.Prototypes.cs +++ b/Content.IntegrationTests/PoolManager.Prototypes.cs @@ -15,11 +15,8 @@ public static partial class PoolManager | BindingFlags.Public | BindingFlags.DeclaredOnly; - private static void DiscoverTestPrototypes(Assembly? assembly = null) + private static void DiscoverTestPrototypes(Assembly assembly) { - assembly ??= typeof(PoolManager).Assembly; - _testPrototypes.Clear(); - foreach (var type in assembly.GetTypes()) { foreach (var field in type.GetFields(Flags)) diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs index 3f489de649..3b49ffcf84 100644 --- a/Content.IntegrationTests/PoolManager.cs +++ b/Content.IntegrationTests/PoolManager.cs @@ -42,6 +42,8 @@ public static partial class PoolManager private static bool _dead; private static Exception? _poolFailureReason; + private static HashSet _contentAssemblies = default!; + public static async Task<(RobustIntegrationTest.ServerIntegrationInstance, PoolTestLogHandler)> GenerateServer( PoolSettings poolSettings, TextWriter testOut) @@ -54,12 +56,7 @@ public static partial class PoolManager LoadConfigAndUserData = false, LoadContentResources = !poolSettings.NoLoadContent, }, - ContentAssemblies = new[] - { - typeof(Shared.Entry.EntryPoint).Assembly, - typeof(Server.Entry.EntryPoint).Assembly, - typeof(PoolManager).Assembly - } + ContentAssemblies = _contentAssemblies.ToArray() }; var logHandler = new PoolTestLogHandler("SERVER"); @@ -140,7 +137,7 @@ public static partial class PoolManager { typeof(Shared.Entry.EntryPoint).Assembly, typeof(Client.Entry.EntryPoint).Assembly, - typeof(PoolManager).Assembly + typeof(PoolManager).Assembly, } }; @@ -422,13 +419,26 @@ we are just going to end this here to save a lot of time. This is the exception /// /// Initialize the pool manager. /// - /// Assembly to search for to discover extra test prototypes. - public static void Startup(Assembly? assembly) + /// Assemblies to search for to discover extra prototypes and systems. + public static void Startup(params Assembly[] extraAssemblies) { if (_initialized) throw new InvalidOperationException("Already initialized"); _initialized = true; - DiscoverTestPrototypes(assembly); + _contentAssemblies = + [ + typeof(Shared.Entry.EntryPoint).Assembly, + typeof(Server.Entry.EntryPoint).Assembly, + typeof(PoolManager).Assembly + ]; + _contentAssemblies.UnionWith(extraAssemblies); + + _testPrototypes.Clear(); + DiscoverTestPrototypes(typeof(PoolManager).Assembly); + foreach (var assembly in extraAssemblies) + { + DiscoverTestPrototypes(assembly); + } } } diff --git a/Content.IntegrationTests/PoolManagerTestEventHandler.cs b/Content.IntegrationTests/PoolManagerTestEventHandler.cs index d37dffff50..3b26d6637f 100644 --- a/Content.IntegrationTests/PoolManagerTestEventHandler.cs +++ b/Content.IntegrationTests/PoolManagerTestEventHandler.cs @@ -13,7 +13,7 @@ public sealed class PoolManagerTestEventHandler [OneTimeSetUp] public void Setup() { - PoolManager.Startup(typeof(PoolManagerTestEventHandler).Assembly); + PoolManager.Startup(); // If the tests seem to be stuck, we try to end it semi-nicely _ = Task.Delay(MaximumTotalTestingTimeLimit).ContinueWith(_ => { diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs index 43dcff2c02..7314119108 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -29,7 +29,7 @@ namespace Content.MapRenderer if (!CommandLineArguments.TryParse(args, out var arguments)) return; - PoolManager.Startup(null); + PoolManager.Startup(); if (arguments.Maps.Count == 0) { Console.WriteLine("Didn't specify any maps to paint! Loading the map list..."); diff --git a/Content.Server/Access/Systems/IdCardSystem.cs b/Content.Server/Access/Systems/IdCardSystem.cs index 9cd9976cea..47388d1a6f 100644 --- a/Content.Server/Access/Systems/IdCardSystem.cs +++ b/Content.Server/Access/Systems/IdCardSystem.cs @@ -27,6 +27,9 @@ public sealed class IdCardSystem : SharedIdCardSystem private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args) { + if (!component.CanMicrowave) + return; + if (TryComp(uid, out var access)) { float randomPick = _random.NextFloat(); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs index f711b235af..5a41a7567b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs @@ -165,7 +165,7 @@ public sealed partial class AtmosphereSystem foreach (var grid in _mapManager.GetAllGrids(playerMap.Value).OrderBy(o => o.Owner)) { var uid = grid.Owner; - if (!TryComp(uid, out var gridXform)) + if (!TryComp(uid, out TransformComponent? gridXform)) continue; options.Add(new CompletionOption(uid.ToString(), $"{MetaData(uid).EntityName} - Map {gridXform.MapID}")); diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs index 3049a77ca3..f3d005c0e7 100644 --- a/Content.Server/Bible/BibleSystem.cs +++ b/Content.Server/Bible/BibleSystem.cs @@ -190,7 +190,7 @@ namespace Content.Server.Bible { Act = () => { - if (!TryComp(args.User, out var userXform)) + if (!TryComp(args.User, out TransformComponent? userXform)) return; AttemptSummon((uid, component), args.User, userXform); @@ -267,7 +267,7 @@ namespace Content.Server.Bible // If this is going to use a ghost role mob spawner, attach it to the bible. if (HasComp(familiar)) { - _popupSystem.PopupEntity(Loc.GetString("bible-summon-requested"), user, PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString("bible-summon-requested"), user, user, PopupType.Medium); _transform.SetParent(familiar, uid); } component.AlreadySummoned = true; diff --git a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs index 61b6f3d93d..d694f84a9c 100644 --- a/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs +++ b/Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs @@ -56,7 +56,7 @@ namespace Content.Server.Engineering.EntitySystems if (component.Deleted || Deleted(uid)) return; - if (!TryComp(uid, out var transformComp)) + if (!TryComp(uid, out TransformComponent? transformComp)) return; var entity = EntityManager.SpawnEntity(component.Prototype, transformComp.Coordinates); diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 8c11de455c..8dc353eb58 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -166,7 +166,7 @@ namespace Content.Server.Explosion.EntitySystems private void HandleGibTrigger(EntityUid uid, GibOnTriggerComponent component, TriggerEvent args) { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) return; if (component.DeleteItems) { diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index e86dbca4a1..16d2d391f6 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -459,7 +459,7 @@ public sealed class FaxSystem : EntitySystem if (sendEntity == null) return; - if (!TryComp(sendEntity, out var metadata) || + if (!TryComp(sendEntity, out MetaDataComponent? metadata) || !TryComp(sendEntity, out var paper)) return; @@ -506,7 +506,7 @@ public sealed class FaxSystem : EntitySystem if (!component.KnownFaxes.TryGetValue(component.DestinationFaxAddress, out var faxName)) return; - if (!TryComp(sendEntity, out var metadata) || + if (!TryComp(sendEntity, out MetaDataComponent? metadata) || !TryComp(sendEntity, out var paper)) return; diff --git a/Content.Server/Gravity/GravityGeneratorSystem.cs b/Content.Server/Gravity/GravityGeneratorSystem.cs index f2ef28b3bb..e4179efb02 100644 --- a/Content.Server/Gravity/GravityGeneratorSystem.cs +++ b/Content.Server/Gravity/GravityGeneratorSystem.cs @@ -45,7 +45,7 @@ namespace Content.Server.Gravity private void OnComponentShutdown(EntityUid uid, GravityGeneratorComponent component, ComponentShutdown args) { if (component.GravityActive && - TryComp(uid, out var xform) && + TryComp(uid, out TransformComponent? xform) && TryComp(xform.ParentUid, out GravityComponent? gravity)) { component.GravityActive = false; @@ -118,7 +118,7 @@ namespace Content.Server.Gravity UpdateUI(ent, chargeRate); if (active != gravGen.GravityActive && - TryComp(uid, out var xform) && + TryComp(uid, out TransformComponent? xform) && TryComp(xform.ParentUid, out var gravity)) { // Force it on in the faster path. diff --git a/Content.Server/Medical/MedicalScannerSystem.cs b/Content.Server/Medical/MedicalScannerSystem.cs index 91184ddc16..b24690e204 100644 --- a/Content.Server/Medical/MedicalScannerSystem.cs +++ b/Content.Server/Medical/MedicalScannerSystem.cs @@ -86,7 +86,7 @@ namespace Content.Server.Medical return; var name = "Unknown"; - if (TryComp(args.Using.Value, out var metadata)) + if (TryComp(args.Using.Value, out MetaDataComponent? metadata)) name = metadata.EntityName; InteractionVerb verb = new() diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs index 95d5c9c465..5daf38c420 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Distance.cs @@ -30,8 +30,8 @@ public sealed partial class PathfindingSystem if (end.GraphUid != start.GraphUid) { - if (!TryComp(start.GraphUid, out var startXform) || - !TryComp(end.GraphUid, out var endXform)) + if (!TryComp(start.GraphUid, out TransformComponent? startXform) || + !TryComp(end.GraphUid, out TransformComponent? endXform)) { return Vector2.Zero; } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs index 6462c10fe5..52f7db77ed 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs @@ -261,7 +261,7 @@ public sealed partial class PathfindingSystem private void OnBodyTypeChange(ref PhysicsBodyTypeChangedEvent ev) { - if (TryComp(ev.Entity, out var xform) && + if (TryComp(ev.Entity, out TransformComponent? xform) && xform.GridUid != null) { var aabb = _lookup.GetAABBNoContainer(ev.Entity, xform.Coordinates.Position, xform.LocalRotation); diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs index a59af88ff5..3672ad047b 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs @@ -264,7 +264,7 @@ namespace Content.Server.NPC.Pathfinding int limit = 40, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var start)) + if (!TryComp(entity, out TransformComponent? start)) return new PathResultEvent(PathResult.NoPath, new List()); var layer = 0; @@ -294,7 +294,7 @@ namespace Content.Server.NPC.Pathfinding CancellationToken cancelToken, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var start)) + if (!TryComp(entity, out TransformComponent? start)) return null; var request = GetRequest(entity, start.Coordinates, end, range, cancelToken, flags); @@ -325,8 +325,8 @@ namespace Content.Server.NPC.Pathfinding CancellationToken cancelToken, PathFlags flags = PathFlags.None) { - if (!TryComp(entity, out var xform) || - !TryComp(target, out var targetXform)) + if (!TryComp(entity, out TransformComponent? xform) || + !TryComp(target, out TransformComponent? targetXform)) return new PathResultEvent(PathResult.NoPath, new List()); var request = GetRequest(entity, xform.Coordinates, targetXform.Coordinates, range, cancelToken, flags); @@ -400,7 +400,7 @@ namespace Content.Server.NPC.Pathfinding var gridUid = coordinates.GetGridUid(EntityManager); if (!TryComp(gridUid, out var comp) || - !TryComp(gridUid, out var xform)) + !TryComp(gridUid, out TransformComponent? xform)) { return null; } diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 4b0ccafa1d..2e8c628b50 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -260,8 +260,8 @@ public sealed class NPCUtilitySystem : EntitySystem { var radius = blackboard.GetValueOrDefault(NPCBlackboard.VisionRadius, EntityManager); - if (!TryComp(targetUid, out var targetXform) || - !TryComp(owner, out var xform)) + if (!TryComp(targetUid, out TransformComponent? targetXform) || + !TryComp(owner, out TransformComponent? xform)) { return 0f; } @@ -308,8 +308,8 @@ public sealed class NPCUtilitySystem : EntitySystem if (blackboard.TryGetValue("Target", out var currentTarget, EntityManager) && currentTarget == targetUid && - TryComp(owner, out var xform) && - TryComp(targetUid, out var targetXform) && + TryComp(owner, out TransformComponent? xform) && + TryComp(targetUid, out TransformComponent? targetXform) && xform.Coordinates.TryDistance(EntityManager, _transform, targetXform.Coordinates, out var distance) && distance <= radius + bufferRange) { diff --git a/Content.Server/PAI/PAISystem.cs b/Content.Server/PAI/PAISystem.cs index 091afb1557..0cdb0bc29a 100644 --- a/Content.Server/PAI/PAISystem.cs +++ b/Content.Server/PAI/PAISystem.cs @@ -111,7 +111,7 @@ public sealed class PAISystem : SharedPAISystem if (TryComp(uid, out var instrument)) _instrumentSystem.Clean(uid, instrument); - if (TryComp(uid, out var metadata)) + if (TryComp(uid, out MetaDataComponent? metadata)) { var proto = metadata.EntityPrototype; if (proto != null) diff --git a/Content.Server/Paper/PaperRandomStoryComponent.cs b/Content.Server/Paper/PaperRandomStoryComponent.cs index 7c5744f087..b8e07f0ee8 100644 --- a/Content.Server/Paper/PaperRandomStoryComponent.cs +++ b/Content.Server/Paper/PaperRandomStoryComponent.cs @@ -1,14 +1,17 @@ +using Content.Shared.StoryGen; +using Robust.Shared.Prototypes; + namespace Content.Server.Paper; /// -/// Adds randomly generated stories to Paper component +/// Adds a randomly generated story to the content of a /// [RegisterComponent, Access(typeof(PaperRandomStorySystem))] public sealed partial class PaperRandomStoryComponent : Component { + /// + /// The ID to use for story generation. + /// [DataField] - public List? StorySegments; - - [DataField] - public string StorySeparator = " "; + public ProtoId Template; } diff --git a/Content.Server/Paper/PaperRandomStorySystem.cs b/Content.Server/Paper/PaperRandomStorySystem.cs index e7712009c2..156718f545 100644 --- a/Content.Server/Paper/PaperRandomStorySystem.cs +++ b/Content.Server/Paper/PaperRandomStorySystem.cs @@ -1,11 +1,11 @@ -using Content.Server.RandomMetadata; +using Content.Shared.StoryGen; namespace Content.Server.Paper; public sealed class PaperRandomStorySystem : EntitySystem { - - [Dependency] private readonly RandomMetadataSystem _randomMeta = default!; + [Dependency] private readonly StoryGeneratorSystem _storyGen = default!; + [Dependency] private readonly PaperSystem _paper = default!; public override void Initialize() { @@ -19,11 +19,9 @@ public sealed class PaperRandomStorySystem : EntitySystem if (!TryComp(paperStory, out var paper)) return; - if (paperStory.Comp.StorySegments == null) + if (!_storyGen.TryGenerateStoryFromTemplate(paperStory.Comp.Template, out var story)) return; - var story = _randomMeta.GetRandomFromSegments(paperStory.Comp.StorySegments, paperStory.Comp.StorySeparator); - - paper.Content += $"\n{story}"; + _paper.SetContent(paperStory.Owner, story, paper); } } diff --git a/Content.Server/Paper/PaperSystem.cs b/Content.Server/Paper/PaperSystem.cs index d10d04cfb9..4a7828c78c 100644 --- a/Content.Server/Paper/PaperSystem.cs +++ b/Content.Server/Paper/PaperSystem.cs @@ -148,7 +148,7 @@ namespace Content.Server.Paper if (TryComp(uid, out var appearance)) _appearance.SetData(uid, PaperVisuals.Status, PaperStatus.Written, appearance); - if (TryComp(uid, out var meta)) + if (TryComp(uid, out MetaDataComponent? meta)) _metaSystem.SetEntityDescription(uid, "", meta); _adminLogger.Add(LogType.Chat, LogImpact.Low, diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 759b8ef29c..6edc202d15 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -271,7 +271,7 @@ namespace Content.Server.Physics.Controllers consoleEnt = cargoConsole.Entity; } - if (!TryComp(consoleEnt, out var xform)) continue; + if (!TryComp(consoleEnt, out TransformComponent? xform)) continue; var gridId = xform.GridUid; // This tries to see if the grid is a shuttle and if the console should work. diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index e6ba1d02af..d4a9159d44 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -248,7 +248,7 @@ public sealed partial class PolymorphSystem : EntitySystem } } - if (configuration.TransferName && TryComp(uid, out var targetMeta)) + if (configuration.TransferName && TryComp(uid, out MetaDataComponent? targetMeta)) _metaData.SetEntityName(child, targetMeta.EntityName); if (configuration.TransferHumanoidAppearance) diff --git a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs index 5d52bde377..0afd86679b 100644 --- a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs +++ b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs @@ -240,7 +240,8 @@ namespace Content.Server.Power.Pow3r } } - if (unmet <= 0 || totalBatterySupply <= 0) + // Return if normal supplies met all demand or there are no supplying batteries + if (unmet <= 0 || totalMaxBatterySupply <= 0) return; // Target output capacity for batteries @@ -275,8 +276,8 @@ namespace Content.Server.Power.Pow3r battery.SupplyRampTarget = battery.MaxEffectiveSupply * relativeTargetBatteryOutput - battery.CurrentReceiving * battery.Efficiency; - DebugTools.Assert(battery.SupplyRampTarget + battery.CurrentReceiving * battery.Efficiency <= battery.LoadingNetworkDemand - || MathHelper.CloseToPercent(battery.SupplyRampTarget + battery.CurrentReceiving * battery.Efficiency, battery.LoadingNetworkDemand, 0.001)); + DebugTools.Assert(battery.MaxEffectiveSupply * relativeTargetBatteryOutput <= battery.LoadingNetworkDemand + || MathHelper.CloseToPercent(battery.MaxEffectiveSupply * relativeTargetBatteryOutput, battery.LoadingNetworkDemand, 0.001)); } } diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 9f87e79505..498d0d5111 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -95,7 +95,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem if (component.DeleteOnCollide) QueueDel(uid); - if (component.ImpactEffect != null && TryComp(uid, out var xform)) + if (component.ImpactEffect != null && TryComp(uid, out TransformComponent? xform)) { RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } diff --git a/Content.Server/Salvage/FultonSystem.cs b/Content.Server/Salvage/FultonSystem.cs index a24bab4584..ad998e5359 100644 --- a/Content.Server/Salvage/FultonSystem.cs +++ b/Content.Server/Salvage/FultonSystem.cs @@ -53,7 +53,7 @@ public sealed class FultonSystem : SharedFultonSystem private void Fulton(EntityUid uid, FultonedComponent component) { if (!Deleted(component.Beacon) && - TryComp(component.Beacon, out var beaconXform) && + TryComp(component.Beacon, out TransformComponent? beaconXform) && !Container.IsEntityOrParentInContainer(component.Beacon.Value, xform: beaconXform) && CanFulton(uid)) { diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs index 23607e2bdc..161b791084 100644 --- a/Content.Server/Salvage/SalvageSystem.Runner.cs +++ b/Content.Server/Salvage/SalvageSystem.Runner.cs @@ -32,7 +32,7 @@ public sealed partial class SalvageSystem private void OnConsoleFTLAttempt(ref ConsoleFTLAttemptEvent ev) { - if (!TryComp(ev.Uid, out var xform) || + if (!TryComp(ev.Uid, out TransformComponent? xform) || !TryComp(xform.MapUid, out var salvage)) { return; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 23f7e10a3e..e87e781e62 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -316,7 +316,7 @@ public sealed class ArrivalsSystem : EntitySystem TryGetArrivals(out var arrivals); - if (TryComp(arrivals, out var arrivalsXform)) + if (TryComp(arrivals, out TransformComponent? arrivalsXform)) { var mapId = arrivalsXform.MapID; @@ -413,7 +413,7 @@ public sealed class ArrivalsSystem : EntitySystem var curTime = _timing.CurTime; TryGetArrivals(out var arrivals); - if (TryComp(arrivals, out var arrivalsXform)) + if (TryComp(arrivals, out TransformComponent? arrivalsXform)) { while (query.MoveNext(out var uid, out var comp, out var shuttle, out var xform)) { diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index e268254d50..4c867e16db 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -267,7 +267,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem if (!Resolve(stationUid, ref stationShuttle)) return; - if (!TryComp(stationShuttle.EmergencyShuttle, out var xform) || + if (!TryComp(stationShuttle.EmergencyShuttle, out TransformComponent? xform) || !TryComp(stationShuttle.EmergencyShuttle, out var shuttle)) { Log.Error($"Attempted to call an emergency shuttle for an uninitialized station? Station: {ToPrettyString(stationUid)}. Shuttle: {ToPrettyString(stationShuttle.EmergencyShuttle)}"); @@ -294,7 +294,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag)) { - if (TryComp(targetGrid.Value, out var targetXform)) + if (TryComp(targetGrid.Value, out TransformComponent? targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); @@ -350,7 +350,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem return; // Post mapinit? fancy - if (TryComp(component.Entity, out var xform)) + if (TryComp(component.Entity, out TransformComponent? xform)) { component.MapEntity = xform.MapUid; return; diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 0d69f132b3..8865b2e312 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -246,7 +246,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem RaiseLocalEvent(entity.Value, ref getShuttleEv); entity = getShuttleEv.Console; - TryComp(entity, out var consoleXform); + TryComp(entity, out TransformComponent? consoleXform); var shuttleGridUid = consoleXform?.GridUid; NavInterfaceState navState; diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs index c4cf2820e2..853548add3 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs @@ -184,7 +184,7 @@ public sealed partial class ShuttleSystem return; if (!TryComp(uid, out var dock) || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || xform.GridUid == null) { return; @@ -196,7 +196,7 @@ public sealed partial class ShuttleSystem if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && ent.Count == 1 && - TryComp(ent[0], out var shuttleXform)) + TryComp(ent[0], out TransformComponent? shuttleXform)) { var escape = GetSingleDock(ent[0]); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs index ce79466b58..ed5d109e85 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.IFF.cs @@ -16,7 +16,7 @@ public sealed partial class ShuttleSystem private void OnIFFShow(EntityUid uid, IFFConsoleComponent component, IFFShowIFFMessage args) { - if (!TryComp(uid, out var xform) || xform.GridUid == null || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid == null || (component.AllowedFlags & IFFFlags.HideLabel) == 0x0) { return; @@ -34,7 +34,7 @@ public sealed partial class ShuttleSystem private void OnIFFShowVessel(EntityUid uid, IFFConsoleComponent component, IFFShowVesselMessage args) { - if (!TryComp(uid, out var xform) || xform.GridUid == null || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid == null || (component.AllowedFlags & IFFFlags.Hide) == 0x0) { return; @@ -54,7 +54,7 @@ public sealed partial class ShuttleSystem { // If we anchor / re-anchor then make sure flags up to date. if (!args.Anchored || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var iff)) { _uiSystem.SetUiState(uid, IFFConsoleUiKey.Key, new IFFConsoleBoundUserInterfaceState() diff --git a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs index 77927c9bba..f5a34728dc 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs @@ -14,7 +14,7 @@ public sealed class SpawnOnDespawnSystem : EntitySystem private void OnDespawn(EntityUid uid, SpawnOnDespawnComponent comp, ref TimedDespawnEvent args) { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) return; Spawn(comp.Prototype, xform.Coordinates); diff --git a/Content.Server/Speech/Components/ReplacementAccentComponent.cs b/Content.Server/Speech/Components/ReplacementAccentComponent.cs index e7f57b80d0..037da72029 100644 --- a/Content.Server/Speech/Components/ReplacementAccentComponent.cs +++ b/Content.Server/Speech/Components/ReplacementAccentComponent.cs @@ -22,6 +22,12 @@ namespace Content.Server.Speech.Components /// [DataField("wordReplacements")] public Dictionary? WordReplacements; + + /// + /// Allows you to substitute words, not always, but with some chance + /// + [DataField] + public float ReplacementChance = 1f; } /// @@ -33,10 +39,5 @@ namespace Content.Server.Speech.Components [DataField("accent", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] public string Accent = default!; - /// - /// Allows you to substitute words, not always, but with some chance - /// - [DataField] - public float ReplacementChance = 1f; } } diff --git a/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs b/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs index da198bcc12..d81d913a36 100644 --- a/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs @@ -24,9 +24,6 @@ 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); } @@ -39,6 +36,9 @@ namespace Content.Server.Speech.EntitySystems if (!_proto.TryIndex(accent, out var prototype)) return message; + if (!_random.Prob(prototype.ReplacementChance)) + return message; + // Prioritize fully replacing if that exists-- // ideally both aren't used at the same time (but we don't have a way to enforce that in serialization yet) if (prototype.FullReplacements != null) diff --git a/Content.Shared/Access/Components/IdCardComponent.cs b/Content.Shared/Access/Components/IdCardComponent.cs index 39d5d9d27f..a099cd26d2 100644 --- a/Content.Shared/Access/Components/IdCardComponent.cs +++ b/Content.Shared/Access/Components/IdCardComponent.cs @@ -46,4 +46,7 @@ public sealed partial class IdCardComponent : Component [DataField] public LocId FullNameLocId = "access-id-card-component-owner-full-name-job-title-text"; + + [DataField] + public bool CanMicrowave = true; } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 537e98ddc8..94e5cefc31 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -577,7 +577,7 @@ namespace Content.Shared.Interaction Ignored? predicate = null, bool popup = false) { - if (!TryComp(other, out var otherXform)) + if (!TryComp(other, out TransformComponent? otherXform)) return false; return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate, @@ -640,7 +640,7 @@ namespace Content.Shared.Interaction fixtureA.FixtureCount > 0 && TryComp(other, out var fixtureB) && fixtureB.FixtureCount > 0 && - TryComp(origin, out var xformA)) + TryComp(origin, out TransformComponent? xformA)) { var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation(); var xfA = new Transform(worldPosA, worldRotA); diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index 8c42511f84..724eca682f 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -113,7 +113,7 @@ public abstract class SharedJetpackSystem : EntitySystem if (args.Handled) return; - if (TryComp(uid, out var xform) && !CanEnableOnGrid(xform.GridUid)) + if (TryComp(uid, out TransformComponent? xform) && !CanEnableOnGrid(xform.GridUid)) { _popup.PopupClient(Loc.GetString("jetpack-no-station"), uid, args.Performer); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs index ba175b345f..1ccb7f0c3f 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs @@ -313,7 +313,7 @@ namespace Content.Shared.Movement.Systems // For stuff like "Moving out of locker" or the likes // We'll relay a movement input to the parent. if (_container.IsEntityInContainer(entity) && - TryComp(entity, out var xform) && + TryComp(entity, out TransformComponent? xform) && xform.ParentUid.IsValid() && _mobState.IsAlive(entity)) { diff --git a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs index d63b8e7326..82d90f4c92 100644 --- a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs @@ -179,7 +179,7 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem var solution = _solutionContainer.SplitSolution(soln.Value, interactions.Volume); // Spray the solution onto the ground and anyone nearby - if (TryComp(entity, out var transform)) + if (TryComp(entity, out TransformComponent? transform)) _puddle.TrySplashSpillAt(entity, transform.Coordinates, solution, out _, sound: false); var drinkName = Identity.Entity(entity, EntityManager); diff --git a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs index 7cae3b9208..bf1e585fab 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs @@ -81,7 +81,7 @@ public abstract partial class SharedDrinkSystem : EntitySystem { string remainingString = "drink-component-on-examine-is-half-full"; - if (TryComp(args.Examiner, out var examiner) && examiner.EntityName.Length > 0 + if (TryComp(args.Examiner, out MetaDataComponent? examiner) && examiner.EntityName.Length > 0 && string.Compare(examiner.EntityName.Substring(0, 1), "m", StringComparison.InvariantCultureIgnoreCase) > 0) remainingString = "drink-component-on-examine-is-half-empty"; diff --git a/Content.Shared/Random/RulesSystem.cs b/Content.Shared/Random/RulesSystem.cs index f8711fb63e..6b8a58abb7 100644 --- a/Content.Shared/Random/RulesSystem.cs +++ b/Content.Shared/Random/RulesSystem.cs @@ -26,7 +26,7 @@ public sealed class RulesSystem : EntitySystem break; case GridInRangeRule griddy: { - if (!TryComp(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) { return false; } @@ -50,7 +50,7 @@ public sealed class RulesSystem : EntitySystem } case InSpaceRule: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid != null) { return false; @@ -146,7 +146,7 @@ public sealed class RulesSystem : EntitySystem } case NearbyEntitiesRule entity: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -177,7 +177,7 @@ public sealed class RulesSystem : EntitySystem } case NearbyTilesPercentRule tiles: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var grid)) { return false; @@ -227,7 +227,7 @@ public sealed class RulesSystem : EntitySystem } case OnMapGridRule: { - if (!TryComp(uid, out var xform) || + if (!TryComp(uid, out TransformComponent? xform) || xform.GridUid != xform.MapUid || xform.MapUid == null) { diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 098f3b9b6c..b44602486e 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -73,7 +73,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, ref LandEvent args) { if (!args.PlaySound || - !TryComp(uid, out var xform) || + !TryComp(uid, out TransformComponent? xform) || !TryComp(xform.GridUid, out var grid)) { return; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 5b07dc3842..c53c3a9e2e 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -471,7 +471,7 @@ public abstract class SharedStorageSystem : EntitySystem return; } - if (_xformQuery.TryGetComponent(uid, out var transformOwner) && TryComp(target, out var transformEnt)) + if (TryComp(uid, out TransformComponent? transformOwner) && TryComp(target, out TransformComponent? transformEnt)) { var parent = transformOwner.ParentUid; diff --git a/Content.Shared/StoryGen/EntitySystems/StoryGeneratorSystem.cs b/Content.Shared/StoryGen/EntitySystems/StoryGeneratorSystem.cs new file mode 100644 index 0000000000..51ad85730c --- /dev/null +++ b/Content.Shared/StoryGen/EntitySystems/StoryGeneratorSystem.cs @@ -0,0 +1,54 @@ +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.Collections; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Shared.StoryGen; + +/// +/// Provides functionality to generate a story from a . +/// +public sealed partial class StoryGeneratorSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + /// + /// Tries to generate a random story using the given template, picking a random word from the referenced + /// datasets for each variable and passing them into the localization system with template. + /// If is specified, the randomizer will be seeded with it for consistent story generation; + /// otherwise the variables will be randomized. + /// Fails if the template prototype cannot be loaded. + /// + /// true if the template was loaded, otherwise false. + public bool TryGenerateStoryFromTemplate(ProtoId template, [NotNullWhen(true)] out string? story, int? seed = null) + { + // Get the story template prototype from the ID + if (!_protoMan.TryIndex(template, out var templateProto)) + { + story = null; + return false; + } + + // If given a seed, use it + if (seed != null) + _random.SetSeed(seed.Value); + + // Pick values for all of the variables in the template + var variables = new ValueList<(string, object)>(templateProto.Variables.Count); + foreach (var (name, list) in templateProto.Variables) + { + // Get the prototype for the world list dataset + if (!_protoMan.TryIndex(list, out var listProto)) + continue; // Missed one, but keep going with the rest of the story + + // Pick a random word from the dataset and localize it + var chosenWord = Loc.GetString(_random.Pick(listProto.Values)); + variables.Add((name, chosenWord)); + } + + // Pass the variables to the localization system and build the story + story = Loc.GetString(templateProto.LocId, variables.ToArray()); + return true; + } +} diff --git a/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs b/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs new file mode 100644 index 0000000000..7f6afacccc --- /dev/null +++ b/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs @@ -0,0 +1,33 @@ +using Content.Shared.Dataset; +using Robust.Shared.Prototypes; + +namespace Content.Shared.StoryGen; + +/// +/// Prototype for a story template that can be filled in with words chosen from s. +/// +[Serializable, Prototype("storyTemplate")] +public sealed partial class StoryTemplatePrototype : IPrototype +{ + /// + /// Identifier for this prototype instance. + /// + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// Localization ID of the Fluent string that forms the structure of this story. + /// + [DataField(required: true)] + public LocId LocId { get; } = default!; + + /// + /// Dictionary containing the name of each variable to pass to the template and the ID of the + /// from which a random entry will be selected as its value. + /// For example, name: book_character will pick a random entry from the book_character + /// dataset which can then be used in the template by {$name}. + /// + [DataField] + public Dictionary> Variables { get; } = default!; +} diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index 3ac8835dd0..a6d27ac545 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -215,7 +215,7 @@ public sealed partial class ActivatableUISystem : EntitySystem if (aui.SingleUser && aui.CurrentSingleUser != null && user != aui.CurrentSingleUser) { var message = Loc.GetString("machine-already-in-use", ("machine", uiEntity)); - _popupSystem.PopupEntity(message, uiEntity, user); + _popupSystem.PopupClient(message, uiEntity, user); if (_uiSystem.IsUiOpen(uiEntity, aui.Key)) return true; diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 96085ebfda..3d9eb67b36 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -348,7 +348,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) { - if (!TryComp(target, out var targetXform)) + if (!TryComp(target, out TransformComponent? targetXform)) return false; return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null); @@ -356,7 +356,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem public bool AttemptDisarmAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) { - if (!TryComp(target, out var targetXform)) + if (!TryComp(target, out TransformComponent? targetXform)) return false; return AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(targetXform.Coordinates)), null); @@ -552,7 +552,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // For consistency with wide attacks stuff needs damageable. if (Deleted(target) || !HasComp(target) || - !TryComp(target, out var targetXform) || + !TryComp(target, out TransformComponent? targetXform) || // Not in LOS. !InRange(user, target.Value, component.Range, session)) { @@ -641,7 +641,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) { // TODO: This is copy-paste as fuck with DoPreciseAttack - if (!TryComp(user, out var userXform)) + if (!TryComp(user, out TransformComponent? userXform)) return false; var targetMap = GetCoordinates(ev.Coordinates).ToMap(EntityManager, TransformSystem); @@ -860,7 +860,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation) { // TODO: Assert that offset eyes are still okay. - if (!TryComp(user, out var userXform)) + if (!TryComp(user, out TransformComponent? userXform)) return; var invMatrix = TransformSystem.GetInvWorldMatrix(userXform); diff --git a/Content.YAMLLinter/Program.cs b/Content.YAMLLinter/Program.cs index 7f0b740fe8..32078faeef 100644 --- a/Content.YAMLLinter/Program.cs +++ b/Content.YAMLLinter/Program.cs @@ -17,7 +17,7 @@ namespace Content.YAMLLinter { private static async Task Main(string[] _) { - PoolManager.Startup(null); + PoolManager.Startup(); var stopwatch = new Stopwatch(); stopwatch.Start(); diff --git a/Resources/Locale/en-US/paper/story-generation.ftl b/Resources/Locale/en-US/paper/story-generation.ftl index bcd1c8901e..94ecbc3caa 100644 --- a/Resources/Locale/en-US/paper/story-generation.ftl +++ b/Resources/Locale/en-US/paper/story-generation.ftl @@ -86,7 +86,7 @@ story-gen-book-character29 = space dragon story-gen-book-character30 = revolutionary story-gen-book-character31 = nuclear operative story-gen-book-character32 = narsie cultist -story-gen-book-character33 = ratwar cultist +story-gen-book-character33 = ratvar cultist story-gen-book-character34 = greytider story-gen-book-character35 = arachnid story-gen-book-character36 = vox @@ -98,7 +98,7 @@ story-gen-book-character40 = slime story-gen-book-character-trait1 = stupid story-gen-book-character-trait2 = smart story-gen-book-character-trait3 = funny -story-gen-book-character-trait4 = attractive +story-gen-book-character-trait4 = attractive story-gen-book-character-trait5 = charming story-gen-book-character-trait6 = nasty story-gen-book-character-trait7 = dying @@ -113,7 +113,7 @@ story-gen-book-character-trait15 = сharismatic story-gen-book-character-trait16 = stoic story-gen-book-character-trait17 = cute story-gen-book-character-trait18 = dwarven -story-gen-book-character-trait19 = beer-smelling +story-gen-book-character-trait19 = beer-smelling story-gen-book-character-trait20 = joyful story-gen-book-character-trait21 = painfully beautiful story-gen-book-character-trait22 = robotic @@ -121,20 +121,20 @@ story-gen-book-character-trait23 = holographic story-gen-book-character-trait24 = hysterically laughing story-gen-book-event1 = a zombie outbreak -story-gen-book-event2 = a nuclear explosion +story-gen-book-event2 = a nuclear explosion story-gen-book-event3 = a mass murder story-gen-book-event4 = a sudden depressurization story-gen-book-event5 = a blackout -story-gen-book-event6 = the starvation of the protagonists +story-gen-book-event6 = the protagonists nearly starving story-gen-book-event7 = a wasting illness story-gen-book-event8 = love at first sight story-gen-book-event9 = a rush of inspiration -story-gen-book-event10 = the occurrence of some mystical phenomena +story-gen-book-event10 = some mystical phenomena story-gen-book-event11 = divine intervention story-gen-book-event12 = the characters' own selfish motives story-gen-book-event13 = an unforeseen deception -story-gen-book-event14 = the resurrection of one of these characters from the dead -story-gen-book-event15 = the terrible torture of the protagonist +story-gen-book-event14 = the resurrection of one of the characters from the dead +story-gen-book-event15 = the brutal torture of the protagonists story-gen-book-event16 = the inadvertent loosing of a gravitational singularity story-gen-book-event17 = a psychic prediction of future events story-gen-book-event18 = an antimatter explosion @@ -145,31 +145,31 @@ story-gen-book-event22 = having a quarrel with a close friend story-gen-book-event23 = the sudden loss of their home in a fiery blaze story-gen-book-event24 = the loss of a PDA -story-gen-book-action1 = share in a kiss with a -story-gen-book-action2 = strangle to death a -story-gen-book-action3 = manage to blow apart a -story-gen-book-action4 = manage to win a game of chess against a -story-gen-book-action5 = narrowly lose a game of chess against a -story-gen-book-action6 = reveal the hidden secrets of a -story-gen-book-action7 = manipulate a -story-gen-book-action8 = sacrifice upon an altar a -story-gen-book-action9 = attend the wedding of a -story-gen-book-action10 = join forces to defeat their common enemy, a -story-gen-book-action11 = are forced to work together to escape a +story-gen-book-action1 = share in a kiss with +story-gen-book-action2 = strangle +story-gen-book-action3 = blow apart +story-gen-book-action4 = win a game of chess against +story-gen-book-action5 = lose a game of chess against +story-gen-book-action6 = reveal the hidden secrets of +story-gen-book-action7 = manipulate +story-gen-book-action8 = sacrifice a hamster to +story-gen-book-action9 = infiltrate the wedding of +story-gen-book-action10 = join forces to defeat their common enemy, +story-gen-book-action11 = are forced to work together to escape story-gen-book-action12 = give a valuable gift to -story-gen-book-action-trait1 = terribly -story-gen-book-action-trait2 = disgustingly +story-gen-book-action-trait1 = clumsily +story-gen-book-action-trait2 = disgustingly story-gen-book-action-trait3 = marvelously story-gen-book-action-trait4 = nicely story-gen-book-action-trait5 = weirdly story-gen-book-action-trait6 = amusingly story-gen-book-action-trait7 = fancifully story-gen-book-action-trait8 = impressively -story-gen-book-action-trait9 = irresponsibly -story-gen-book-action-trait10 = severely -story-gen-book-action-trait11 = ruthlessly -story-gen-book-action-trait12 = playfully +story-gen-book-action-trait9 = irresponsibly +story-gen-book-action-trait10 = severely +story-gen-book-action-trait11 = ruthlessly +story-gen-book-action-trait12 = playfully story-gen-book-action-trait13 = thoughtfully story-gen-book-location1 = in an underground complex @@ -178,7 +178,7 @@ story-gen-book-location3 = while trapped in outer space story-gen-book-location4 = while in a news office story-gen-book-location5 = in a hidden garden story-gen-book-location6 = in the kitchen of a local restaurant -story-gen-book-location7 = under the counter of the local sports bar +story-gen-book-location7 = under the counter of the local sports bar story-gen-book-location8 = in an ancient library story-gen-book-location9 = while deep in bowels of the space station's maintenance corridors story-gen-book-location10 = on the bridge of a starship @@ -192,7 +192,7 @@ story-gen-book-location17 = standing too close to an anomaly story-gen-book-location18 = while huddling on the evacuation shuttle story-gen-book-location19 = standing in freshly fallen snow story-gen-book-location20 = lost in the woods -story-gen-book-location21 = iin the harsh desert +story-gen-book-location21 = in the harsh desert story-gen-book-location22 = worrying about their social media networks story-gen-book-location23 = atop of a mountain story-gen-book-location24 = while driving a car @@ -207,15 +207,15 @@ story-gen-book-location32 = while trapped in a shadow dimension story-gen-book-location33 = while trying to escape a destroyed space station story-gen-book-location34 = while sandwiched between a Tesla ball and a gravitational singularity -story-gen-book-element1 = The plot -story-gen-book-element2 = The twist -story-gen-book-element3 = The climax -story-gen-book-element4 = The final act -story-gen-book-element5 = The ending -story-gen-book-element6 = The moral of the story -story-gen-book-element7 = The theme of this work -story-gen-book-element8 = The literary style -story-gen-book-element9 = The illustrations +story-gen-book-element1 = plot +story-gen-book-element2 = twist +story-gen-book-element3 = climax +story-gen-book-element4 = final act +story-gen-book-element5 = ending +story-gen-book-element6 = moral of the story +story-gen-book-element7 = theme of this work +story-gen-book-element8 = literary style +story-gen-book-element9 = artwork story-gen-book-element-trait1 = terrifying story-gen-book-element-trait2 = disgusting diff --git a/Resources/Locale/en-US/storygen/story-template.ftl b/Resources/Locale/en-US/storygen/story-template.ftl new file mode 100644 index 0000000000..b535e2fd95 --- /dev/null +++ b/Resources/Locale/en-US/storygen/story-template.ftl @@ -0,0 +1,4 @@ +story-template-generic = + This is { INDEFINITE($bookGenre) } {$bookGenre} about { INDEFINITE($char1Adj) } {$char1Adj} {$char1Type} and { INDEFINITE($char2Adj) } {$char2Adj} {$char2Type}. Due to {$event}, they {$actionTrait} {$action} { INDEFINITE($char3Type) } {$char3Type} {$location}. + + The {$element} is {$elementTrait}. diff --git a/Resources/Prototypes/Accents/word_replacements.yml b/Resources/Prototypes/Accents/word_replacements.yml index 2a90d7b405..80d1158c61 100644 --- a/Resources/Prototypes/Accents/word_replacements.yml +++ b/Resources/Prototypes/Accents/word_replacements.yml @@ -431,6 +431,7 @@ - type: accent id: liar + replacementChance: 0.15 wordReplacements: liar-word-1: liar-word-replacement-1 liar-word-2: liar-word-replacement-2 @@ -474,4 +475,4 @@ liar-word-39: liar-word-replacement-39 liar-word-40: liar-word-replacement-40 liar-word-41: liar-word-replacement-41 - liar-word-42: liar-word-replacement-42 \ No newline at end of file + liar-word-42: liar-word-replacement-42 diff --git a/Resources/Prototypes/Datasets/story_generation.yml b/Resources/Prototypes/Datasets/story_generation.yml index 1083a6acdb..1a461c7596 100644 --- a/Resources/Prototypes/Datasets/story_generation.yml +++ b/Resources/Prototypes/Datasets/story_generation.yml @@ -1,31 +1,31 @@ - type: dataset - id: book_type + id: BookTypes values: - - story-gen-book-type1 - - story-gen-book-type2 - - story-gen-book-type3 - - story-gen-book-type4 - - story-gen-book-type5 - - story-gen-book-type6 - - story-gen-book-type7 - - story-gen-book-type8 - - story-gen-book-type9 + - story-gen-book-type1 + - story-gen-book-type2 + - story-gen-book-type3 + - story-gen-book-type4 + - story-gen-book-type5 + - story-gen-book-type6 + - story-gen-book-type7 + - story-gen-book-type8 + - story-gen-book-type9 - story-gen-book-type10 - story-gen-book-type11 - story-gen-book-type12 - type: dataset - id: book_genre + id: BookGenres values: - - story-gen-book-genre1 - - story-gen-book-genre2 - - story-gen-book-genre3 - - story-gen-book-genre4 - - story-gen-book-genre5 - - story-gen-book-genre6 - - story-gen-book-genre7 - - story-gen-book-genre8 - - story-gen-book-genre9 + - story-gen-book-genre1 + - story-gen-book-genre2 + - story-gen-book-genre3 + - story-gen-book-genre4 + - story-gen-book-genre5 + - story-gen-book-genre6 + - story-gen-book-genre7 + - story-gen-book-genre8 + - story-gen-book-genre9 - story-gen-book-genre10 - story-gen-book-genre11 - story-gen-book-genre12 @@ -33,17 +33,17 @@ - story-gen-book-genre14 - type: dataset - id: book_hint_appearance + id: BookHintAppearances values: - - story-gen-book-appearance1 - - story-gen-book-appearance2 - - story-gen-book-appearance3 - - story-gen-book-appearance4 - - story-gen-book-appearance5 - - story-gen-book-appearance6 - - story-gen-book-appearance7 - - story-gen-book-appearance8 - - story-gen-book-appearance9 + - story-gen-book-appearance1 + - story-gen-book-appearance2 + - story-gen-book-appearance3 + - story-gen-book-appearance4 + - story-gen-book-appearance5 + - story-gen-book-appearance6 + - story-gen-book-appearance7 + - story-gen-book-appearance8 + - story-gen-book-appearance9 - story-gen-book-appearance10 - story-gen-book-appearance11 - story-gen-book-appearance12 @@ -64,17 +64,17 @@ - story-gen-book-appearance27 - type: dataset - id: book_character + id: BookCharacters values: - - story-gen-book-character1 - - story-gen-book-character2 - - story-gen-book-character3 - - story-gen-book-character4 - - story-gen-book-character5 - - story-gen-book-character6 - - story-gen-book-character7 - - story-gen-book-character8 - - story-gen-book-character9 + - story-gen-book-character1 + - story-gen-book-character2 + - story-gen-book-character3 + - story-gen-book-character4 + - story-gen-book-character5 + - story-gen-book-character6 + - story-gen-book-character7 + - story-gen-book-character8 + - story-gen-book-character9 - story-gen-book-character10 - story-gen-book-character11 - story-gen-book-character12 @@ -108,17 +108,17 @@ - story-gen-book-character40 - type: dataset - id: book_character_trait + id: BookCharacterTraits values: - - story-gen-book-character-trait1 - - story-gen-book-character-trait2 - - story-gen-book-character-trait3 - - story-gen-book-character-trait4 - - story-gen-book-character-trait5 - - story-gen-book-character-trait6 - - story-gen-book-character-trait7 - - story-gen-book-character-trait8 - - story-gen-book-character-trait9 + - story-gen-book-character-trait1 + - story-gen-book-character-trait2 + - story-gen-book-character-trait3 + - story-gen-book-character-trait4 + - story-gen-book-character-trait5 + - story-gen-book-character-trait6 + - story-gen-book-character-trait7 + - story-gen-book-character-trait8 + - story-gen-book-character-trait9 - story-gen-book-character-trait10 - story-gen-book-character-trait11 - story-gen-book-character-trait12 @@ -137,17 +137,17 @@ - type: dataset - id: book_event + id: BookEvents values: - - story-gen-book-event1 - - story-gen-book-event2 - - story-gen-book-event3 - - story-gen-book-event4 - - story-gen-book-event5 - - story-gen-book-event6 - - story-gen-book-event7 - - story-gen-book-event8 - - story-gen-book-event9 + - story-gen-book-event1 + - story-gen-book-event2 + - story-gen-book-event3 + - story-gen-book-event4 + - story-gen-book-event5 + - story-gen-book-event6 + - story-gen-book-event7 + - story-gen-book-event8 + - story-gen-book-event9 - story-gen-book-event10 - story-gen-book-event11 - story-gen-book-event12 @@ -165,50 +165,50 @@ - story-gen-book-event24 - type: dataset - id: book_action + id: BookActions values: - - story-gen-book-action1 - - story-gen-book-action2 - - story-gen-book-action3 - - story-gen-book-action4 - - story-gen-book-action5 - - story-gen-book-action6 - - story-gen-book-action7 - - story-gen-book-action8 - - story-gen-book-action9 + - story-gen-book-action1 + - story-gen-book-action2 + - story-gen-book-action3 + - story-gen-book-action4 + - story-gen-book-action5 + - story-gen-book-action6 + - story-gen-book-action7 + - story-gen-book-action8 + - story-gen-book-action9 - story-gen-book-action10 - story-gen-book-action11 - story-gen-book-action12 - type: dataset - id: book_action_trait + id: BookActionTraits values: - - story-gen-book-action-trait1 - - story-gen-book-action-trait2 - - story-gen-book-action-trait3 - - story-gen-book-action-trait4 - - story-gen-book-action-trait5 - - story-gen-book-action-trait6 - - story-gen-book-action-trait7 - - story-gen-book-action-trait8 - - story-gen-book-action-trait9 + - story-gen-book-action-trait1 + - story-gen-book-action-trait2 + - story-gen-book-action-trait3 + - story-gen-book-action-trait4 + - story-gen-book-action-trait5 + - story-gen-book-action-trait6 + - story-gen-book-action-trait7 + - story-gen-book-action-trait8 + - story-gen-book-action-trait9 - story-gen-book-action-trait10 - story-gen-book-action-trait11 - story-gen-book-action-trait12 - story-gen-book-action-trait13 - type: dataset - id: book_location + id: BookLocations values: - - story-gen-book-location1 - - story-gen-book-location2 - - story-gen-book-location3 - - story-gen-book-location4 - - story-gen-book-location5 - - story-gen-book-location6 - - story-gen-book-location7 - - story-gen-book-location8 - - story-gen-book-location9 + - story-gen-book-location1 + - story-gen-book-location2 + - story-gen-book-location3 + - story-gen-book-location4 + - story-gen-book-location5 + - story-gen-book-location6 + - story-gen-book-location7 + - story-gen-book-location8 + - story-gen-book-location9 - story-gen-book-location10 - story-gen-book-location11 - story-gen-book-location12 @@ -236,7 +236,7 @@ - story-gen-book-location34 - type: dataset - id: book_story_element + id: BookStoryElements values: - story-gen-book-element1 - story-gen-book-element2 @@ -249,18 +249,18 @@ - story-gen-book-element9 - type: dataset - id: book_story_element_trait + id: BookStoryElementTraits values: - - story-gen-book-element-trait1 - - story-gen-book-element-trait2 - - story-gen-book-element-trait3 - - story-gen-book-element-trait4 - - story-gen-book-element-trait5 - - story-gen-book-element-trait6 - - story-gen-book-element-trait7 - - story-gen-book-element-trait8 - - story-gen-book-element-trait9 + - story-gen-book-element-trait1 + - story-gen-book-element-trait2 + - story-gen-book-element-trait3 + - story-gen-book-element-trait4 + - story-gen-book-element-trait5 + - story-gen-book-element-trait6 + - story-gen-book-element-trait7 + - story-gen-book-element-trait8 + - story-gen-book-element-trait9 - story-gen-book-element-trait10 - story-gen-book-element-trait11 - story-gen-book-element-trait12 - - story-gen-book-element-trait13 \ No newline at end of file + - story-gen-book-element-trait13 diff --git a/Resources/Prototypes/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index 25e6bb9f94..3fc90048dd 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books.yml @@ -361,8 +361,8 @@ components: - type: RandomMetadata nameSegments: - - book_hint_appearance - - book_type + - BookHintAppearances + - BookTypes - type: RandomSprite available: - cover: @@ -423,33 +423,7 @@ suffix: random visual, random story components: - type: PaperRandomStory - storySegments: - - "This is a " - - book_genre - - " about a " - - book_character_trait - - " " - - book_character - - " and " - - book_character_trait - - " " - - book_character - - ". Due to " - - book_event - - ", they " - - book_action_trait - - " " - - book_action - - " " - - book_character - - " " - - book_location - - ". \n\n" - - book_story_element - - " is " - - book_story_element_trait - - "." - storySeparator: "" + template: GenericStory - type: entity parent: BookBase diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index d293467cee..fb52a6e879 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -452,10 +452,10 @@ noSpawn: true components: - type: StationEvent - earliestStart: 25 + earliestStart: 30 weight: 8 minimumPlayers: 15 - reoccurrenceDelay: 30 + maxOccurrences: 1 # can only happen once per round startAnnouncement: station-event-communication-interception startAudio: path: /Audio/Announcements/intercept.ogg diff --git a/Resources/Prototypes/StoryGen/story-templates.yml b/Resources/Prototypes/StoryGen/story-templates.yml new file mode 100644 index 0000000000..f05fd5afa6 --- /dev/null +++ b/Resources/Prototypes/StoryGen/story-templates.yml @@ -0,0 +1,16 @@ +- type: storyTemplate + id: GenericStory + locId: story-template-generic + variables: + bookGenre: BookGenres + char1Type: BookCharacters + char1Adj: BookCharacterTraits + char2Type: BookCharacters + char2Adj: BookCharacterTraits + event: BookEvents + action: BookActions + actionTrait: BookActionTraits + char3Type: BookCharacters + location: BookLocations + element: BookStoryElements + elementTrait: BookStoryElementTraits diff --git a/Resources/Prototypes/Traits/neutral.yml b/Resources/Prototypes/Traits/neutral.yml index e7dd8bfb3e..e9420522ef 100644 --- a/Resources/Prototypes/Traits/neutral.yml +++ b/Resources/Prototypes/Traits/neutral.yml @@ -1,4 +1,4 @@ -- type: trait +- type: trait id: PirateAccent name: trait-pirate-accent-name description: trait-pirate-accent-desc