Merge remote-tracking branch 'space-station-14/master' into ed-19-05-2024-upstream
# Conflicts: # Resources/Prototypes/Entities/Structures/Walls/asteroid.yml
273
Content.Benchmarks/ComponentQueryBenchmark.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for comparing the speed of various component fetching/lookup related methods, including directed event
|
||||
/// subscriptions
|
||||
/// </summary>
|
||||
[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<ItemComponent> _itemQuery;
|
||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||
private EntityQuery<MapComponent> _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<IEntityManager>();
|
||||
|
||||
_itemQuery = _entMan.GetEntityQuery<ItemComponent>();
|
||||
_clothingQuery = _entMan.GetEntityQuery<ClothingComponent>();
|
||||
_mapQuery = _entMan.GetEntityQuery<MapComponent>();
|
||||
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
||||
var i = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out _))
|
||||
{
|
||||
_items[i++] = uid;
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task Cleanup()
|
||||
{
|
||||
await _pair.DisposeAsync();
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
#region TryComp
|
||||
|
||||
/// <summary>
|
||||
/// Baseline TryComp benchmark. When the benchmark was created, around 40% of the items were clothing.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that is meant to always fail to get a component.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that is meant to always succeed getting a component.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that uses `Resolve()` to try get the component.
|
||||
/// </summary>
|
||||
[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<ItemComponent>();
|
||||
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<ClothingComponent, ItemComponent>();
|
||||
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<ClothingComponent, ItemComponent, TransformComponent>();
|
||||
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<AirlockComponent>();
|
||||
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<AirlockComponent, DoorComponent>();
|
||||
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<AirlockComponent, DoorComponent, TransformComponent>();
|
||||
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<ClothingComponent, QueryBenchEvent>(OnEvent);
|
||||
}
|
||||
|
||||
private void OnEvent(EntityUid uid, ClothingComponent component, ref QueryBenchEvent args)
|
||||
{
|
||||
args.HashCode = HashCode.Combine(args.HashCode, component.GetHashCode());
|
||||
}
|
||||
}
|
||||
@@ -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<ClothingComponent> _clothingQuery;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup(null);
|
||||
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_clothingQuery = _entMan.GetEntityQuery<ClothingComponent>();
|
||||
|
||||
// Apparently ~40% of entities are items, and 1 in 6 of those are clothing.
|
||||
/*
|
||||
var entCount = _entMan.EntityCount;
|
||||
var itemCount = _entMan.Count<ItemComponent>();
|
||||
var clothingCount = _entMan.Count<ClothingComponent>();
|
||||
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<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_entMan.HasComponent<ClothingComponent>(uid))
|
||||
hashCode = HashCode.Combine(hashCode, uid.Id);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int HasComponentQuery()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
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<ItemComponent>();
|
||||
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<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_clothingQuery.TryGetComponent(uid, out var clothing))
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate all entities with both an item and clothing component.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public int Enumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent, ClothingComponent>();
|
||||
while (enumerator.MoveNext(out var _, out var clothing))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<IEntityManager>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Content.Client.FlavorText
|
||||
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
CFlavorTextInput.Placeholder = new Rope.Leaf(loc.GetString("flavor-text-placeholder"));
|
||||
CFlavorTextInput.OnKeyBindDown += _ => FlavorTextChanged();
|
||||
CFlavorTextInput.OnTextChanged += _ => FlavorTextChanged();
|
||||
}
|
||||
|
||||
public void FlavorTextChanged()
|
||||
|
||||
@@ -215,6 +215,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(ContentKeyFunctions.OpenInventoryMenu);
|
||||
AddButton(ContentKeyFunctions.OpenAHelp);
|
||||
AddButton(ContentKeyFunctions.OpenActionsMenu);
|
||||
AddButton(ContentKeyFunctions.OpenEmotesMenu);
|
||||
AddButton(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
AddButton(ContentKeyFunctions.OpenEntitySpawnWindow);
|
||||
AddButton(ContentKeyFunctions.OpenSandboxWindow);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -42,6 +42,8 @@ public static partial class PoolManager
|
||||
private static bool _dead;
|
||||
private static Exception? _poolFailureReason;
|
||||
|
||||
private static HashSet<Assembly> _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
|
||||
/// <summary>
|
||||
/// Initialize the pool manager.
|
||||
/// </summary>
|
||||
/// <param name="assembly">Assembly to search for to discover extra test prototypes.</param>
|
||||
public static void Startup(Assembly? assembly)
|
||||
/// <param name="extraAssemblies">Assemblies to search for to discover extra prototypes and systems.</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(_ =>
|
||||
{
|
||||
|
||||
@@ -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...");
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace Content.Server.Bible
|
||||
// If this is going to use a ghost role mob spawner, attach it to the bible.
|
||||
if (HasComp<GhostRoleMobSpawnerComponent>(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;
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using Content.Shared.StoryGen;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Paper;
|
||||
|
||||
/// <summary>
|
||||
/// Adds randomly generated stories to Paper component
|
||||
/// Adds a randomly generated story to the content of a <see cref="PaperComponent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(PaperRandomStorySystem))]
|
||||
public sealed partial class PaperRandomStoryComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="StoryTemplatePrototype"/> ID to use for story generation.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<string>? StorySegments;
|
||||
|
||||
[DataField]
|
||||
public string StorySeparator = " ";
|
||||
public ProtoId<StoryTemplatePrototype> Template;
|
||||
}
|
||||
|
||||
@@ -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<PaperComponent>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ namespace Content.Server.Speech.Components
|
||||
/// </summary>
|
||||
[DataField("wordReplacements")]
|
||||
public Dictionary<string, string>? WordReplacements;
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to substitute words, not always, but with some chance
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ReplacementChance = 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -33,10 +39,5 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ReplacementAccentPrototype>(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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
|
||||
namespace Content.Shared.Hands.Components;
|
||||
|
||||
@@ -20,6 +21,15 @@ public static class HandHelpers
|
||||
/// </summary>
|
||||
public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
|
||||
/// cache it instead of accessing this multiple times.
|
||||
/// </summary>
|
||||
public static int CountFreeableHands(this Entity<HandsComponent> component, SharedHandsSystem system)
|
||||
{
|
||||
return system.CountFreeableHands(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
|
||||
/// it instead of accessing this multiple times.
|
||||
|
||||
@@ -5,7 +5,6 @@ using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Input.Binding;
|
||||
@@ -299,4 +298,16 @@ public abstract partial class SharedHandsSystem
|
||||
|
||||
return hands.Hands.TryGetValue(handId, out hand);
|
||||
}
|
||||
|
||||
public int CountFreeableHands(Entity<HandsComponent> hands)
|
||||
{
|
||||
var freeable = 0;
|
||||
foreach (var hand in hands.Comp.Hands.Values)
|
||||
{
|
||||
if (hand.IsEmpty || CanDropHeld(hands, hand))
|
||||
freeable++;
|
||||
}
|
||||
|
||||
return freeable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -29,6 +30,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string VirtualItem = "VirtualItem";
|
||||
@@ -71,23 +73,53 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
}
|
||||
|
||||
#region Hands
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a virtual item in a empty hand
|
||||
/// </summary>
|
||||
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
|
||||
/// <param name="user">The entity that we want to insert the virtual entity</param>
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
|
||||
/// <param name="dropOthers">Whether or not to try and drop other items to make space</param>
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, bool dropOthers = false)
|
||||
{
|
||||
return TrySpawnVirtualItemInHand(blockingEnt, user, out _);
|
||||
return TrySpawnVirtualItemInHand(blockingEnt, user, out _, dropOthers);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid)"/>
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
|
||||
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,bool)"/>
|
||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false)
|
||||
{
|
||||
if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand))
|
||||
virtualItem = null;
|
||||
if (!_handsSystem.TryGetEmptyHand(user, out var empty))
|
||||
{
|
||||
if (!dropOthers)
|
||||
return false;
|
||||
|
||||
foreach (var hand in _handsSystem.EnumerateHands(user))
|
||||
{
|
||||
if (hand.HeldEntity is not { } held)
|
||||
continue;
|
||||
|
||||
if (held == blockingEnt || HasComp<VirtualItemComponent>(held))
|
||||
continue;
|
||||
|
||||
if (!_handsSystem.TryDrop(user, hand))
|
||||
continue;
|
||||
|
||||
if (!TerminatingOrDeleted(held))
|
||||
_popup.PopupClient(Loc.GetString("virtual-item-dropped-other", ("dropped", held)), user, user);
|
||||
|
||||
empty = hand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty == null)
|
||||
return false;
|
||||
|
||||
_handsSystem.DoPickup(user, hand, virtualItem.Value);
|
||||
if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem))
|
||||
return false;
|
||||
|
||||
_handsSystem.DoPickup(user, empty, virtualItem.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -120,6 +152,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
|
||||
/// <param name="user">The entity that we want to insert the virtual entity</param>
|
||||
/// <param name="slot">The slot to which we will insert the virtual entity (could be the "shoes" slot, for example)</param>
|
||||
/// <param name="force">Whether or not to force an equip</param>
|
||||
public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false)
|
||||
{
|
||||
return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _);
|
||||
@@ -140,6 +173,8 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
/// that's done check if the found virtual entity is a copy of our matching entity,
|
||||
/// if it is, delete it
|
||||
/// </summary>
|
||||
/// <param name="user">The entity that we want to delete the virtual entity from</param>
|
||||
/// <param name="matching">The entity that made the virtual entity</param>
|
||||
/// <param name="slotName">Set this param if you have the name of the slot, it avoids unnecessary queries</param>
|
||||
public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null)
|
||||
{
|
||||
@@ -178,6 +213,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
|
||||
/// <param name="user">The entity that we want to insert the virtual entity</param>
|
||||
/// <param name="virtualItem">The virtual item, if spawned</param>
|
||||
public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
|
||||
{
|
||||
if (_netManager.IsClient)
|
||||
|
||||
@@ -384,6 +384,7 @@ namespace Content.Shared.Preferences
|
||||
if (!_antagPreferences.SequenceEqual(other._antagPreferences)) return false;
|
||||
if (!_traitPreferences.SequenceEqual(other._traitPreferences)) return false;
|
||||
if (!Loadouts.SequenceEqual(other.Loadouts)) return false;
|
||||
if (FlavorText != other.FlavorText) return false;
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared.StoryGen;
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality to generate a story from a <see cref="StoryTemplatePrototype"/>.
|
||||
/// </summary>
|
||||
public sealed partial class StoryGeneratorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 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 <paramref name="seed"/> 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.
|
||||
/// </summary>
|
||||
/// <returns>true if the template was loaded, otherwise false.</returns>
|
||||
public bool TryGenerateStoryFromTemplate(ProtoId<StoryTemplatePrototype> 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;
|
||||
}
|
||||
}
|
||||
33
Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.Dataset;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.StoryGen;
|
||||
|
||||
/// <summary>
|
||||
/// Prototype for a story template that can be filled in with words chosen from <see cref="DatasetPrototype"/>s.
|
||||
/// </summary>
|
||||
[Serializable, Prototype("storyTemplate")]
|
||||
public sealed partial class StoryTemplatePrototype : IPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifier for this prototype instance.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Localization ID of the Fluent string that forms the structure of this story.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public LocId LocId { get; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary containing the name of each variable to pass to the template and the ID of the
|
||||
/// <see cref="DatasetPrototype"/> from which a random entry will be selected as its value.
|
||||
/// For example, <c>name: book_character</c> will pick a random entry from the book_character
|
||||
/// dataset which can then be used in the template by <c>{$name}</c>.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, ProtoId<DatasetPrototype>> Variables { get; } = default!;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -161,7 +161,7 @@ public sealed class WieldableSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hands.CountFreeHands() < component.FreeHandsRequired)
|
||||
if (_handsSystem.CountFreeableHands((user, hands)) < component.FreeHandsRequired)
|
||||
{
|
||||
if (!quiet)
|
||||
{
|
||||
@@ -202,9 +202,21 @@ public sealed class WieldableSystem : EntitySystem
|
||||
if (component.WieldSound != null)
|
||||
_audioSystem.PlayPredicted(component.WieldSound, used, user);
|
||||
|
||||
var virtuals = new List<EntityUid>();
|
||||
for (var i = 0; i < component.FreeHandsRequired; i++)
|
||||
{
|
||||
_virtualItemSystem.TrySpawnVirtualItemInHand(used, user);
|
||||
if (_virtualItemSystem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true))
|
||||
{
|
||||
virtuals.Add(virtualItem.Value);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var existingVirtual in virtuals)
|
||||
{
|
||||
QueueDel(existingVirtual);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryComp(used, out UseDelayComponent? useDelay)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Content.YAMLLinter
|
||||
{
|
||||
private static async Task<int> Main(string[] _)
|
||||
{
|
||||
PoolManager.Startup(null);
|
||||
PoolManager.Startup();
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
|
||||
@@ -1,43 +1,4 @@
|
||||
Entries:
|
||||
- author: Just_Art
|
||||
changes:
|
||||
- message: Added bob 5 hair!
|
||||
type: Add
|
||||
- message: Added long hair with bundles!
|
||||
type: Add
|
||||
id: 6100
|
||||
time: '2024-03-06T01:42:05.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25772
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: Debug coordinates are now automatically enabled on the F3 overlay upon
|
||||
readmin.
|
||||
type: Fix
|
||||
id: 6101
|
||||
time: '2024-03-06T01:43:51.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25063
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Pod launches will now be offset slightly.
|
||||
type: Add
|
||||
id: 6102
|
||||
time: '2024-03-06T01:44:26.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25855
|
||||
- author: Brandon-Huu
|
||||
changes:
|
||||
- message: The Syndicate is now supplying barber scissors in the Hristov bundle
|
||||
to make your disguises even balde- better.
|
||||
type: Tweak
|
||||
id: 6103
|
||||
time: '2024-03-06T02:03:08.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25695
|
||||
- author: Tayrtahn
|
||||
changes:
|
||||
- message: Flasks can once again be put into dispensers.
|
||||
type: Fix
|
||||
id: 6104
|
||||
time: '2024-03-06T17:19:01.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25883
|
||||
- author: Doctor-Cpu
|
||||
changes:
|
||||
- message: Cargo palllets can no longer be ordered from cargo request terminal
|
||||
@@ -3868,3 +3829,42 @@
|
||||
id: 6599
|
||||
time: '2024-05-17T07:51:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/27960
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Fixed bug with pathological liar and zombie accents.
|
||||
type: Fix
|
||||
id: 6600
|
||||
time: '2024-05-17T16:33:22.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28049
|
||||
- author: YuNii
|
||||
changes:
|
||||
- message: Added the ability to re-bind the emote menu.
|
||||
type: Add
|
||||
id: 6601
|
||||
time: '2024-05-17T19:46:45.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28083
|
||||
- author: benjamin-burges
|
||||
changes:
|
||||
- message: Bible summon messages are now only visible to the chaplain using the
|
||||
Bible.
|
||||
type: Tweak
|
||||
id: 6602
|
||||
time: '2024-05-18T14:23:17.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28104
|
||||
- author: Aquif
|
||||
changes:
|
||||
- message: Changing your character's description now properly updates the save button.
|
||||
type: Fix
|
||||
- message: The character description editor does not discard your last edit anymore.
|
||||
type: Fix
|
||||
id: 6603
|
||||
time: '2024-05-19T00:23:45.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28122
|
||||
- author: DrSmugleaf
|
||||
changes:
|
||||
- message: Attempting to dual wield something will now automatically drop the item
|
||||
in your other hand.
|
||||
type: Tweak
|
||||
id: 6604
|
||||
time: '2024-05-19T01:35:46.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/27975
|
||||
|
||||
@@ -173,6 +173,7 @@ ui-options-function-open-crafting-menu = Open crafting menu
|
||||
ui-options-function-open-inventory-menu = Open inventory
|
||||
ui-options-function-open-a-help = Open admin help
|
||||
ui-options-function-open-abilities-menu = Open action menu
|
||||
ui-options-function-open-emotes-menu = Open emotes menu
|
||||
ui-options-function-toggle-round-end-summary-window = Toggle round end summary window
|
||||
ui-options-function-open-entity-spawn-window = Open entity spawn menu
|
||||
ui-options-function-open-sandbox-window = Open sandbox menu
|
||||
|
||||
@@ -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
|
||||
|
||||
4
Resources/Locale/en-US/storygen/story-template.ftl
Normal file
@@ -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}.
|
||||
1
Resources/Locale/en-US/virtual/virtual-item.ftl
Normal file
@@ -0,0 +1 @@
|
||||
virtual-item-dropped-other = You dropped {THE($dropped)}!
|
||||
@@ -428,6 +428,7 @@
|
||||
|
||||
- type: accent
|
||||
id: liar
|
||||
replacementChance: 0.15
|
||||
wordReplacements:
|
||||
liar-word-1: liar-word-replacement-1
|
||||
liar-word-2: liar-word-replacement-2
|
||||
@@ -471,4 +472,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
|
||||
liar-word-42: liar-word-replacement-42
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
- type: cargoProduct
|
||||
id: ServiceBureaucracy
|
||||
icon:
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
sprite: Objects/Misc/pens.rsi
|
||||
state: pen
|
||||
product: CrateServiceBureaucracy
|
||||
cost: 1000
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
id: UplinkExplodingPen
|
||||
name: uplink-exploding-pen-name
|
||||
description: uplink-exploding-pen-desc
|
||||
icon: { sprite: /Textures/Objects/Misc/bureaucracy.rsi, state: pen }
|
||||
icon: { sprite: /Textures/Objects/Misc/pens.rsi, state: pen }
|
||||
productEntity: PenExplodingBox
|
||||
cost:
|
||||
Telecrystal: 4
|
||||
@@ -472,7 +472,7 @@
|
||||
id: UplinkHypopen
|
||||
name: uplink-hypopen-name
|
||||
description: uplink-hypopen-desc
|
||||
icon: { sprite: /Textures/Objects/Misc/bureaucracy.rsi, state: pen }
|
||||
icon: { sprite: /Textures/Objects/Misc/pens.rsi, state: pen }
|
||||
productEntity: HypopenBox
|
||||
cost:
|
||||
Telecrystal: 6
|
||||
@@ -1715,4 +1715,4 @@
|
||||
- !type:BuyerWhitelistCondition
|
||||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- SurplusBundle
|
||||
|
||||
@@ -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
|
||||
- story-gen-book-element-trait13
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -283,119 +283,6 @@
|
||||
components:
|
||||
- type: NukeCodePaper
|
||||
|
||||
- type: entity
|
||||
name: pen
|
||||
parent: BaseItem
|
||||
id: Pen
|
||||
description: 'A dark ink pen.'
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Write
|
||||
- Pen
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
state: pen
|
||||
- type: Item
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
heldPrefix: pen
|
||||
size: Tiny
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Steel: 25
|
||||
|
||||
- type: entity
|
||||
parent: Pen
|
||||
id: PenEmbeddable
|
||||
abstract: true
|
||||
components:
|
||||
- type: EmbeddableProjectile
|
||||
offset: 0.3,0.0
|
||||
removalTime: 0.0
|
||||
- type: ThrowingAngle
|
||||
angle: 315
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
Piercing: 3
|
||||
|
||||
#TODO: I want the luxury pen to write a cool font like Merriweather in the future.
|
||||
|
||||
- type: entity
|
||||
name: luxury pen
|
||||
parent: Pen
|
||||
id: LuxuryPen
|
||||
description: A fancy and expensive pen that you only deserve to own if you're qualified to handle vast amounts of paperwork.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: luxury_pen
|
||||
- type: Item
|
||||
heldPrefix: luxury_pen
|
||||
|
||||
- type: entity
|
||||
name: Cybersun pen
|
||||
parent: PenEmbeddable
|
||||
id: CyberPen
|
||||
description: A high-tech pen straight from Cybersun's legal department, capable of refracting hard-light at impossible angles through its diamond tip in order to write. So powerful, it's even able to rewrite officially stamped documents should the need arise.
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Write
|
||||
- WriteIgnoreStamps
|
||||
- Pickaxe
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
state: overpriced_pen
|
||||
- type: MeleeWeapon
|
||||
wideAnimationRotation: -45
|
||||
damage:
|
||||
types:
|
||||
Piercing: 15
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
- type: Tool
|
||||
qualities:
|
||||
- Screwing
|
||||
useSound:
|
||||
collection: Screwdriver
|
||||
- type: Item
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
heldPrefix: overpriced_pen
|
||||
size: Tiny
|
||||
|
||||
- type: entity
|
||||
name: captain's fountain pen
|
||||
parent: PenEmbeddable
|
||||
id: PenCap
|
||||
description: 'A luxurious fountain pen for the captain of the station.'
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
state: pen_cap
|
||||
|
||||
- type: entity
|
||||
name: CentCom pen
|
||||
parent: CyberPen
|
||||
id: PenCentcom
|
||||
description: In an attempt to keep up with the "power" of the cybersun bureaucracy, NT made a replica of cyber pen, in their corporate style.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
state: pen_centcom
|
||||
- type: Item
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
heldPrefix: pen_centcom
|
||||
|
||||
- type: entity
|
||||
name: hop's fountain pen
|
||||
parent: PenEmbeddable
|
||||
id: PenHop
|
||||
description: 'A luxurious fountain pen for the hop of the station.'
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
state: pen_hop
|
||||
|
||||
- type: entity
|
||||
id: BoxFolderBase
|
||||
parent: BoxBase
|
||||
|
||||
106
Resources/Prototypes/Entities/Objects/Misc/pen.yml
Normal file
@@ -0,0 +1,106 @@
|
||||
- type: entity
|
||||
name: pen
|
||||
parent: BaseItem
|
||||
id: Pen
|
||||
description: A dark ink pen.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/pens.rsi
|
||||
state: pen
|
||||
- type: Item
|
||||
sprite: Objects/Misc/pens.rsi
|
||||
heldPrefix: pen
|
||||
size: Tiny
|
||||
- type: Tag
|
||||
tags:
|
||||
- Write
|
||||
- Pen
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Steel: 25
|
||||
|
||||
- type: entity
|
||||
parent: Pen
|
||||
id: PenEmbeddable
|
||||
abstract: true
|
||||
components:
|
||||
- type: EmbeddableProjectile
|
||||
offset: 0.3,0.0
|
||||
removalTime: 0.0
|
||||
- type: ThrowingAngle
|
||||
angle: 315
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
Piercing: 3
|
||||
|
||||
#TODO: I want the luxury pen to write a cool font like Merriweather in the future.
|
||||
|
||||
- type: entity
|
||||
name: luxury pen
|
||||
parent: Pen
|
||||
id: LuxuryPen
|
||||
description: A fancy and expensive pen that you only deserve to own if you're qualified to handle vast amounts of paperwork.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: luxury_pen
|
||||
- type: Item
|
||||
heldPrefix: luxury_pen
|
||||
|
||||
- type: entity
|
||||
name: Cybersun pen
|
||||
parent: PenEmbeddable
|
||||
id: CyberPen
|
||||
description: A high-tech pen straight from Cybersun's legal department, capable of refracting hard-light at impossible angles through its diamond tip in order to write. So powerful, it's even able to rewrite officially stamped documents should the need arise.
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Write
|
||||
- WriteIgnoreStamps
|
||||
- Pickaxe
|
||||
- Pen
|
||||
- type: Sprite
|
||||
state: overpriced_pen
|
||||
- type: Item
|
||||
heldPrefix: overpriced_pen
|
||||
- type: MeleeWeapon
|
||||
wideAnimationRotation: -45
|
||||
damage:
|
||||
types:
|
||||
Piercing: 15
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
- type: Tool
|
||||
qualities:
|
||||
- Screwing
|
||||
useSound:
|
||||
collection: Screwdriver
|
||||
|
||||
- type: entity
|
||||
name: captain's fountain pen
|
||||
parent: PenEmbeddable
|
||||
id: PenCap
|
||||
description: A luxurious fountain pen for the captain of the station.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: pen_cap
|
||||
|
||||
- type: entity
|
||||
name: CentCom pen
|
||||
parent: CyberPen
|
||||
id: PenCentcom
|
||||
description: In an attempt to keep up with the "power" of the cybersun bureaucracy, NT made a replica of cyber pen, in their corporate style.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: pen_centcom
|
||||
- type: Item
|
||||
heldPrefix: pen_centcom
|
||||
|
||||
- type: entity
|
||||
name: hop's fountain pen
|
||||
parent: PenEmbeddable
|
||||
id: PenHop
|
||||
description: A luxurious fountain pen for the hop of the station.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: pen_hop
|
||||
@@ -374,7 +374,7 @@
|
||||
- type: Transform
|
||||
noRot: true
|
||||
- type: SoundOnGather
|
||||
- type: Gatherable
|
||||
- type: Gatherable
|
||||
toolWhitelist:
|
||||
tags:
|
||||
- Pickaxe
|
||||
|
||||
16
Resources/Prototypes/StoryGen/story-templates.yml
Normal file
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
- type: trait
|
||||
- type: trait
|
||||
id: PirateAccent
|
||||
name: trait-pirate-accent-name
|
||||
description: trait-pirate-accent-desc
|
||||
@@ -30,5 +30,4 @@
|
||||
description: trait-liar-desc
|
||||
components:
|
||||
- type: ReplacementAccent
|
||||
replacementChance: 0.15
|
||||
accent: liar
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
# TODO: Kill ore veins
|
||||
# Split it into 2 components, 1 for "spawn XYZ on destruction" and 1 for "randomly select one of these for spawn on destruction"
|
||||
# You could even just use an entityspawncollection instead.
|
||||
- type: ore
|
||||
id: SpaceShrooms
|
||||
oreEntity: FoodSpaceshroom
|
||||
|
||||
# High yields
|
||||
- type: ore
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. paper_stamp-syndicate by Veritius. paper_receipt, paper_receipt_horizontal by eoineoineoin. pen_centcom is a resprited version of pen_cap by PuroSlavKing (Github). Luxury pen is drawn by Ubaser.",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. paper_stamp-syndicate by Veritius. paper_receipt, paper_receipt_horizontal by eoineoineoin.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
@@ -159,38 +159,6 @@
|
||||
{
|
||||
"name": "paper_dotmatrix_words"
|
||||
},
|
||||
{
|
||||
"name": "pen"
|
||||
},
|
||||
{
|
||||
"name": "pen_cap"
|
||||
},
|
||||
{
|
||||
"name": "pen_centcom"
|
||||
},
|
||||
{
|
||||
"name": "pen_hop"
|
||||
},
|
||||
{
|
||||
"name": "overpriced_pen"
|
||||
},
|
||||
{
|
||||
"name": "luxury_pen"
|
||||
},
|
||||
{
|
||||
"name": "pen_blue"
|
||||
},
|
||||
{
|
||||
"name": "pen_red"
|
||||
},
|
||||
{
|
||||
"name": "pen-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "pen-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "scrap"
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
59
Resources/Textures/Objects/Misc/pens.rsi/meta.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. pen_centcom is a resprited version of pen_cap by PuroSlavKing (Github). Luxury pen is drawn by Ubaser.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "pen"
|
||||
},
|
||||
{
|
||||
"name": "pen_cap"
|
||||
},
|
||||
{
|
||||
"name": "pen_centcom"
|
||||
},
|
||||
{
|
||||
"name": "pen_hop"
|
||||
},
|
||||
{
|
||||
"name": "overpriced_pen"
|
||||
},
|
||||
{
|
||||
"name": "luxury_pen"
|
||||
},
|
||||
{
|
||||
"name": "pen_blue"
|
||||
},
|
||||
{
|
||||
"name": "pen_red"
|
||||
},
|
||||
{
|
||||
"name": "pen-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "pen-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "overpriced_pen-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "overpriced_pen-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "pen_centcom-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "pen_centcom-inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 236 B |
|
After Width: | Height: | Size: 236 B |
|
Before Width: | Height: | Size: 317 B After Width: | Height: | Size: 317 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 283 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 283 B |
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 222 B |
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
|
Before Width: | Height: | Size: 343 B After Width: | Height: | Size: 343 B |
|
After Width: | Height: | Size: 227 B |
|
After Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |