Merge branch 'master' into offmed-staging

This commit is contained in:
Janet Blackquill
2025-10-09 22:34:06 -04:00
41 changed files with 2174 additions and 665 deletions

View File

@@ -1,8 +0,0 @@
using Content.Shared.PAI;
namespace Content.Client.PAI
{
public sealed class PAISystem : SharedPAISystem
{
}
}

View File

@@ -1,7 +1,7 @@
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Shared.Body.Systems;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared; using Robust.Shared;

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
namespace Content.IntegrationTests.Tests.Helpers;
/// <summary>
/// Component that is used by <see cref="TestListenerSystem{TEvent}"/> to store any information about received events.
/// </summary>
[RegisterComponent]
public sealed partial class TestListenerComponent : Component
{
public Dictionary<Type, List<object>> Events = new();
}

View File

@@ -0,0 +1,45 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.Helpers;
/// <summary>
/// Generic system that listens for and records any received events of a given type.
/// </summary>
public abstract class TestListenerSystem<TEvent> : EntitySystem where TEvent : notnull
{
public override void Initialize()
{
// TODO
// supporting broadcast events requires cleanup on test finish, which will probably require changes to the
// test pair/pool manager and would conflict with #36797
SubscribeLocalEvent<TestListenerComponent, TEvent>(OnDirectedEvent);
}
protected virtual void OnDirectedEvent(Entity<TestListenerComponent> ent, ref TEvent args)
{
ent.Comp.Events.GetOrNew(args.GetType()).Add(args);
}
public int Count(EntityUid uid, Func<TEvent, bool>? predicate = null)
{
return GetEvents(uid, predicate).Count();
}
public void Clear(EntityUid uid)
{
CompOrNull<TestListenerComponent>(uid)?.Events.Remove(typeof(TEvent));
}
public IEnumerable<TEvent> GetEvents(EntityUid uid, Func<TEvent, bool>? predicate = null)
{
var events = CompOrNull<TestListenerComponent>(uid)?.Events.GetValueOrDefault(typeof(TEvent));
if (events == null)
return [];
return events.Cast<TEvent>().Where(e => predicate?.Invoke(e) ?? true);
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
using Content.Client.Construction; using Content.Client.Construction;
using Content.IntegrationTests.Tests.Helpers;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Construction.Components; using Content.Server.Construction.Components;
using Content.Server.Gravity; using Content.Server.Gravity;
@@ -22,6 +23,8 @@ using Robust.Shared.Input;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Reflection;
using Robust.UnitTesting;
using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent; using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent;
namespace Content.IntegrationTests.Tests.Interaction; namespace Content.IntegrationTests.Tests.Interaction;
@@ -29,6 +32,8 @@ namespace Content.IntegrationTests.Tests.Interaction;
// This partial class defines various methods that are useful for performing & validating interactions // This partial class defines various methods that are useful for performing & validating interactions
public abstract partial class InteractionTest public abstract partial class InteractionTest
{ {
private Dictionary<Type, EntitySystem> _listenerCache = new();
/// <summary> /// <summary>
/// Begin constructing an entity. /// Begin constructing an entity.
/// </summary> /// </summary>
@@ -758,6 +763,139 @@ public abstract partial class InteractionTest
#endregion #endregion
#region EventListener
/// <summary>
/// Asserts that running the given action causes an event to be fired directed at the specified entity (defaults to <see cref="Target"/>).
/// </summary>
/// <remarks>
/// This currently only checks server-side events.
/// </remarks>
/// <param name="uid">The entity at which the events are supposed to be directed</param>
/// <param name="count">How many new events are expected</param>
/// <param name="clear">Whether to clear all previously recorded events before invoking the delegate</param>
protected async Task AssertFiresEvent<TEvent>(Func<Task> act, EntityUid? uid = null, int count = 1, bool clear = true)
where TEvent : notnull
{
var sys = GetListenerSystem<TEvent>();
uid ??= STarget;
if (uid == null)
{
Assert.Fail("No target specified");
return;
}
if (clear)
sys.Clear(uid.Value);
else
count += sys.Count(uid.Value);
await Server.WaitPost(() => SEntMan.EnsureComponent<TestListenerComponent>(uid.Value));
await act();
AssertEvent<TEvent>(uid, count: count);
}
/// <summary>
/// This is a variant of <see cref="AssertFiresEvent{TEvent}"/> that passes the delegate to <see cref="RobustIntegrationTest.ServerIntegrationInstance.WaitPost"/>.
/// </summary>
/// <remarks>
/// This currently only checks for server-side events.
/// </remarks>
/// <param name="uid">The entity at which the events are supposed to be directed</param>
/// <param name="count">How many new events are expected</param>
/// <param name="clear">Whether to clear all previously recorded events before invoking the delegate</param>
protected async Task AssertPostFiresEvent<TEvent>(Action act, EntityUid? uid = null, int count = 1, bool clear = true)
where TEvent : notnull
{
await AssertFiresEvent<TEvent>(async () => await Server.WaitPost(act), uid, count, clear);
}
/// <summary>
/// This is a variant of <see cref="AssertFiresEvent{TEvent}"/> that passes the delegate to <see cref="RobustIntegrationTest.ServerIntegrationInstance.WaitAssertion"/>.
/// </summary>
/// <remarks>
/// This currently only checks for server-side events.
/// </remarks>
/// <param name="uid">The entity at which the events are supposed to be directed</param>
/// <param name="count">How many new events are expected</param>
/// <param name="clear">Whether to clear all previously recorded events before invoking the delegate</param>
protected async Task AssertAssertionFiresEvent<TEvent>(Action act,
EntityUid? uid = null,
int count = 1,
bool clear = true)
where TEvent : notnull
{
await AssertFiresEvent<TEvent>(async () => await Server.WaitAssertion(act), uid, count, clear);
}
/// <summary>
/// Asserts that the specified event has been fired some number of times at the given entity (defaults to <see cref="Target"/>).
/// For this to work, this requires that the entity has been given a <see cref="TestListenerComponent"/>
/// </summary>
/// <remarks>
/// This currently only checks server-side events.
/// </remarks>
/// <param name="uid">The entity at which the events were directed</param>
/// <param name="count">How many new events are expected</param>
/// <param name="predicate">A predicate that can be used to filter the recorded events</param>
protected void AssertEvent<TEvent>(EntityUid? uid = null, int count = 1, Func<TEvent,bool>? predicate = null)
where TEvent : notnull
{
Assert.That(GetEvents(uid, predicate).Count, Is.EqualTo(count));
}
/// <summary>
/// Gets all the events of the specified type that have been fired at the given entity (defaults to <see cref="Target"/>).
/// For this to work, this requires that the entity has been given a <see cref="TestListenerComponent"/>
/// </summary>
/// <remarks>
/// This currently only gets for server-side events.
/// </remarks>
/// <param name="uid">The entity at which the events were directed</param>
/// <param name="predicate">A predicate that can be used to filter the returned events</param>
protected IEnumerable<TEvent> GetEvents<TEvent>(EntityUid? uid = null, Func<TEvent, bool>? predicate = null)
where TEvent : notnull
{
uid ??= STarget;
if (uid == null)
{
Assert.Fail("No target specified");
return [];
}
Assert.That(SEntMan.HasComponent<TestListenerComponent>(uid), $"Entity must have {nameof(TestListenerComponent)}");
return GetListenerSystem<TEvent>().GetEvents(uid.Value, predicate);
}
protected TestListenerSystem<TEvent> GetListenerSystem<TEvent>()
where TEvent : notnull
{
if (_listenerCache.TryGetValue(typeof(TEvent), out var listener))
return (TestListenerSystem<TEvent>) listener;
var type = Server.Resolve<IReflectionManager>().GetAllChildren<TestListenerSystem<TEvent>>().Single();
if (!SEntMan.EntitySysManager.TryGetEntitySystem(type, out var systemObj))
{
// There has to be a listener system that is manually defined. Event subscriptions are locked once
// finalized, so we can't really easily create new subscriptions on the fly.
// TODO find a better solution
throw new InvalidOperationException($"Event {typeof(TEvent).Name} has no associated listener system!");
}
var system = (TestListenerSystem<TEvent>)systemObj;
_listenerCache[typeof(TEvent)] = system;
return system;
}
/// <summary>
/// Clears all recorded events of the given type.
/// </summary>
protected void ClearEvents<TEvent>(EntityUid uid) where TEvent : notnull
=> GetListenerSystem<TEvent>().Clear(uid);
#endregion
#region Entity lookups #region Entity lookups
/// <summary> /// <summary>

View File

@@ -1,10 +1,8 @@
#nullable enable #nullable enable
using System.Collections.Generic; using Content.IntegrationTests.Tests.Helpers;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Slippery; using Content.Shared.Slippery;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Robust.Shared.GameObjects;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -12,44 +10,32 @@ namespace Content.IntegrationTests.Tests.Movement;
public sealed class SlippingTest : MovementTest public sealed class SlippingTest : MovementTest
{ {
public sealed class SlipTestSystem : EntitySystem public sealed class SlipTestSystem : TestListenerSystem<SlipEvent>;
{
public HashSet<EntityUid> Slipped = new();
public override void Initialize()
{
SubscribeLocalEvent<SlipperyComponent, SlipEvent>(OnSlip);
}
private void OnSlip(EntityUid uid, SlipperyComponent component, ref SlipEvent args)
{
Slipped.Add(args.Slipped);
}
}
[Test] [Test]
public async Task BananaSlipTest() public async Task BananaSlipTest()
{ {
var sys = SEntMan.System<SlipTestSystem>();
await SpawnTarget("TrashBananaPeel"); await SpawnTarget("TrashBananaPeel");
var modifier = Comp<MovementSpeedModifierComponent>(Player).SprintSpeedModifier; var modifier = Comp<MovementSpeedModifierComponent>(Player).SprintSpeedModifier;
Assert.That(modifier, Is.EqualTo(1), "Player is not moving at full speed."); Assert.That(modifier, Is.EqualTo(1), "Player is not moving at full speed.");
// Player is to the left of the banana peel and has not slipped. // Player is to the left of the banana peel.
Assert.That(Delta(), Is.GreaterThan(0.5f)); Assert.That(Delta(), Is.GreaterThan(0.5f));
Assert.That(sys.Slipped, Does.Not.Contain(SEntMan.GetEntity(Player)));
// Walking over the banana slowly does not trigger a slip. // Walking over the banana slowly does not trigger a slip.
await SetKey(EngineKeyFunctions.Walk, BoundKeyState.Down); await SetKey(EngineKeyFunctions.Walk, BoundKeyState.Down);
await Move(DirectionFlag.East, 1f); await AssertFiresEvent<SlipEvent>(async () => await Move(DirectionFlag.East, 1f), count: 0);
Assert.That(Delta(), Is.LessThan(0.5f)); Assert.That(Delta(), Is.LessThan(0.5f));
Assert.That(sys.Slipped, Does.Not.Contain(SEntMan.GetEntity(Player)));
AssertComp<KnockedDownComponent>(false, Player); AssertComp<KnockedDownComponent>(false, Player);
// Moving at normal speeds does trigger a slip. // Moving at normal speeds does trigger a slip.
await SetKey(EngineKeyFunctions.Walk, BoundKeyState.Up); await SetKey(EngineKeyFunctions.Walk, BoundKeyState.Up);
await Move(DirectionFlag.West, 1f); await AssertFiresEvent<SlipEvent>(async () => await Move(DirectionFlag.West, 1f));
Assert.That(sys.Slipped, Does.Contain(SEntMan.GetEntity(Player)));
// And the person that slipped was the player
AssertEvent<SlipEvent>(predicate: @event => @event.Slipped == SPlayer);
AssertComp<KnockedDownComponent>(true, Player); AssertComp<KnockedDownComponent>(true, Player);
} }
} }

View File

@@ -25,8 +25,6 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
public float[] GasSpecificHeats => _gasSpecificHeats; public float[] GasSpecificHeats => _gasSpecificHeats;
public string?[] GasReagents = new string[Atmospherics.TotalNumberOfGases];
private void InitializeGases() private void InitializeGases()
{ {
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray(); _gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray();
@@ -37,7 +35,6 @@ namespace Content.Server.Atmos.EntitySystems
for (var i = 0; i < GasPrototypes.Length; i++) for (var i = 0; i < GasPrototypes.Length; i++)
{ {
_gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale; _gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale;
GasReagents[i] = GasPrototypes[i].Reagent;
} }
} }

View File

@@ -3,6 +3,7 @@ using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.EntityEffects; using Content.Server.EntityEffects;
using Content.Shared.Body.Systems;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;

View File

@@ -2,27 +2,22 @@ using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Instruments; using Content.Server.Instruments;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Server.Store.Systems;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.PAI; using Content.Shared.PAI;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Content.Shared.Instruments; using Content.Shared.Instruments;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using System.Text; using System.Text;
namespace Content.Server.PAI; namespace Content.Server.PAI;
public sealed class PAISystem : SharedPAISystem public sealed class PAISystem : EntitySystem
{ {
[Dependency] private readonly InstrumentSystem _instrumentSystem = default!; [Dependency] private readonly InstrumentSystem _instrumentSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StoreSystem _store = default!;
[Dependency] private readonly ToggleableGhostRoleSystem _toggleableGhostRole = default!; [Dependency] private readonly ToggleableGhostRoleSystem _toggleableGhostRole = default!;
/// <summary> /// <summary>
@@ -38,8 +33,6 @@ public sealed class PAISystem : SharedPAISystem
SubscribeLocalEvent<PAIComponent, MindAddedMessage>(OnMindAdded); SubscribeLocalEvent<PAIComponent, MindAddedMessage>(OnMindAdded);
SubscribeLocalEvent<PAIComponent, MindRemovedMessage>(OnMindRemoved); SubscribeLocalEvent<PAIComponent, MindRemovedMessage>(OnMindRemoved);
SubscribeLocalEvent<PAIComponent, BeingMicrowavedEvent>(OnMicrowaved); SubscribeLocalEvent<PAIComponent, BeingMicrowavedEvent>(OnMicrowaved);
SubscribeLocalEvent<PAIComponent, PAIShopActionEvent>(OnShop);
} }
private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args) private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args)
@@ -106,15 +99,6 @@ public sealed class PAISystem : SharedPAISystem
var val = Loc.GetString("pai-system-pai-name-raw", ("name", name.ToString())); var val = Loc.GetString("pai-system-pai-name-raw", ("name", name.ToString()));
_metaData.SetEntityName(uid, val); _metaData.SetEntityName(uid, val);
} }
private void OnShop(Entity<PAIComponent> ent, ref PAIShopActionEvent args)
{
if (!TryComp<StoreComponent>(ent, out var store))
return;
_store.ToggleUi(args.Performer, ent, store);
}
public void PAITurningOff(EntityUid uid) public void PAITurningOff(EntityUid uid)
{ {
// Close the instrument interface if it was open // Close the instrument interface if it was open

View File

@@ -1,7 +1,6 @@
using System.Numerics; using System.Numerics;
using Content.Server.Actions; using Content.Server.Actions;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Store.Components;
using Content.Server.Store.Systems; using Content.Server.Store.Systems;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
@@ -21,7 +20,6 @@ using Content.Shared.Store.Components;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Revenant.EntitySystems; namespace Content.Server.Revenant.EntitySystems;
@@ -46,17 +44,12 @@ public sealed partial class RevenantSystem : EntitySystem
[Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly VisibilitySystem _visibility = default!; [Dependency] private readonly VisibilitySystem _visibility = default!;
[Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly TurfSystem _turf = default!;
private static readonly EntProtoId RevenantShopId = "ActionRevenantShop";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RevenantComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<RevenantComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<RevenantComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<RevenantComponent, RevenantShopActionEvent>(OnShop);
SubscribeLocalEvent<RevenantComponent, DamageChangedEvent>(OnDamage); SubscribeLocalEvent<RevenantComponent, DamageChangedEvent>(OnDamage);
SubscribeLocalEvent<RevenantComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<RevenantComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded); SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded);
@@ -94,11 +87,6 @@ public sealed partial class RevenantSystem : EntitySystem
_eye.RefreshVisibilityMask(uid); _eye.RefreshVisibilityMask(uid);
} }
private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)
{
_action.AddAction(uid, ref component.Action, RevenantShopId);
}
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args) private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
{ {
if (args.Key == "Stun") if (args.Key == "Stun")
@@ -182,13 +170,6 @@ public sealed partial class RevenantSystem : EntitySystem
return true; return true;
} }
private void OnShop(EntityUid uid, RevenantComponent component, RevenantShopActionEvent args)
{
if (!TryComp<StoreComponent>(uid, out var store))
return;
_store.ToggleUi(uid, uid, store);
}
public void MakeVisible(bool visible) public void MakeVisible(bool visible)
{ {
var query = EntityQueryEnumerator<RevenantComponent, VisibilityComponent>(); var query = EntityQueryEnumerator<RevenantComponent, VisibilityComponent>();

View File

@@ -1,17 +1,16 @@
using System.Linq;
using Content.Server.Store.Components; using Content.Server.Store.Components;
using Content.Shared.UserInterface;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Implants.Components; using Content.Shared.Implants.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Store.Components; using Content.Shared.Store.Components;
using JetBrains.Annotations; using Content.Shared.Store.Events;
using Content.Shared.UserInterface;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using System.Linq;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Shared.Mind; using Robust.Shared.Utility;
namespace Content.Server.Store.Systems; namespace Content.Server.Store.Systems;
@@ -37,6 +36,7 @@ public sealed partial class StoreSystem : EntitySystem
SubscribeLocalEvent<StoreComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<StoreComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<StoreComponent, ComponentShutdown>(OnShutdown); SubscribeLocalEvent<StoreComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<StoreComponent, OpenUplinkImplantEvent>(OnImplantActivate); SubscribeLocalEvent<StoreComponent, OpenUplinkImplantEvent>(OnImplantActivate);
SubscribeLocalEvent<StoreComponent, IntrinsicStoreActionEvent>(OnIntrinsicStoreAction);
InitializeUi(); InitializeUi();
InitializeCommand(); InitializeCommand();
@@ -187,6 +187,12 @@ public sealed partial class StoreSystem : EntitySystem
UpdateUserInterface(null, uid, store); UpdateUserInterface(null, uid, store);
return true; return true;
} }
private void OnIntrinsicStoreAction(Entity<StoreComponent> ent, ref IntrinsicStoreActionEvent args)
{
ToggleUi(args.Performer, ent.Owner, ent.Comp);
}
} }
public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs

View File

@@ -1,4 +1,5 @@
using Content.Shared.Damage; using Content.Shared.Clothing.Components;
using Content.Shared.Damage;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs;
@@ -32,6 +33,9 @@ public abstract class SharedArmorSystem : EntitySystem
/// <param name="args">The event, contains the running count of armor percentage as a coefficient</param> /// <param name="args">The event, contains the running count of armor percentage as a coefficient</param>
private void OnCoefficientQuery(Entity<ArmorComponent> ent, ref InventoryRelayedEvent<CoefficientQueryEvent> args) private void OnCoefficientQuery(Entity<ArmorComponent> ent, ref InventoryRelayedEvent<CoefficientQueryEvent> args)
{ {
if (TryComp<MaskComponent>(ent, out var mask) && mask.IsToggled)
return;
foreach (var armorCoefficient in ent.Comp.Modifiers.Coefficients) foreach (var armorCoefficient in ent.Comp.Modifiers.Coefficients)
{ {
args.Args.DamageModifiers.Coefficients[armorCoefficient.Key] = args.Args.DamageModifiers.Coefficients.TryGetValue(armorCoefficient.Key, out var coefficient) ? coefficient * armorCoefficient.Value : armorCoefficient.Value; args.Args.DamageModifiers.Coefficients[armorCoefficient.Key] = args.Args.DamageModifiers.Coefficients.TryGetValue(armorCoefficient.Key, out var coefficient) ? coefficient * armorCoefficient.Value : armorCoefficient.Value;
@@ -40,12 +44,18 @@ public abstract class SharedArmorSystem : EntitySystem
private void OnDamageModify(EntityUid uid, ArmorComponent component, InventoryRelayedEvent<DamageModifyEvent> args) private void OnDamageModify(EntityUid uid, ArmorComponent component, InventoryRelayedEvent<DamageModifyEvent> args)
{ {
if (TryComp<MaskComponent>(uid, out var mask) && mask.IsToggled)
return;
args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers);
} }
private void OnBorgDamageModify(EntityUid uid, ArmorComponent component, private void OnBorgDamageModify(EntityUid uid, ArmorComponent component,
ref BorgModuleRelayedEvent<DamageModifyEvent> args) ref BorgModuleRelayedEvent<DamageModifyEvent> args)
{ {
if (TryComp<MaskComponent>(uid, out var mask) && mask.IsToggled)
return;
args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers); args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers);
} }

View File

@@ -13,6 +13,8 @@ namespace Content.Shared.Atmos.EntitySystems
private EntityQuery<InternalsComponent> _internalsQuery; private EntityQuery<InternalsComponent> _internalsQuery;
public string?[] GasReagents = new string[Atmospherics.TotalNumberOfGases];
protected readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases]; protected readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases];
public override void Initialize() public override void Initialize()
@@ -26,6 +28,7 @@ namespace Content.Shared.Atmos.EntitySystems
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{ {
GasPrototypes[i] = _prototypeManager.Index<GasPrototype>(i.ToString()); GasPrototypes[i] = _prototypeManager.Index<GasPrototype>(i.ToString());
GasReagents[i] = GasPrototypes[i].Reagent;
} }
} }

View File

@@ -59,6 +59,8 @@ public sealed partial class SleepingSystem : EntitySystem
SubscribeLocalEvent<SleepingComponent, EntityZombifiedEvent>(OnZombified); SubscribeLocalEvent<SleepingComponent, EntityZombifiedEvent>(OnZombified);
SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<SleepingComponent, ComponentInit>(OnCompInit); SubscribeLocalEvent<SleepingComponent, ComponentInit>(OnCompInit);
SubscribeLocalEvent<SleepingComponent, ComponentRemove>(OnComponentRemoved);
SubscribeLocalEvent<SleepingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt); SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt); SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt); SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt);
@@ -69,7 +71,6 @@ public sealed partial class SleepingSystem : EntitySystem
SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand); SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt); SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt);
SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt); SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt);
SubscribeLocalEvent<SleepingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied); SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt); SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
@@ -102,6 +103,12 @@ public sealed partial class SleepingSystem : EntitySystem
TrySleeping((ent, ent.Comp)); TrySleeping((ent, ent.Comp));
} }
private void OnRejuvenate(Entity<SleepingComponent> ent, ref RejuvenateEvent args)
{
// WAKE UP!!!
RemComp<SleepingComponent>(ent);
}
/// <summary> /// <summary>
/// when sleeping component is added or removed, we do some stuff with other components. /// when sleeping component is added or removed, we do some stuff with other components.
/// </summary> /// </summary>
@@ -143,6 +150,16 @@ public sealed partial class SleepingSystem : EntitySystem
_actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent); _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent);
} }
private void OnComponentRemoved(Entity<SleepingComponent> ent, ref ComponentRemove args)
{
_actionsSystem.RemoveAction(ent.Owner, ent.Comp.WakeAction);
var ev = new SleepStateChangedEvent(false);
RaiseLocalEvent(ent, ref ev);
_blindableSystem.UpdateIsBlind(ent.Owner);
}
private void OnSpeakAttempt(Entity<SleepingComponent> ent, ref SpeakAttemptEvent args) private void OnSpeakAttempt(Entity<SleepingComponent> ent, ref SpeakAttemptEvent args)
{ {
// TODO reduce duplication of this behavior with MobStateSystem somehow // TODO reduce duplication of this behavior with MobStateSystem somehow
@@ -187,11 +204,6 @@ public sealed partial class SleepingSystem : EntitySystem
args.Cancelled = true; args.Cancelled = true;
} }
private void OnRejuvenate(Entity<SleepingComponent> ent, ref RejuvenateEvent args)
{
TryWaking((ent.Owner, ent.Comp), true);
}
private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args) private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args)
{ {
if (args.IsInDetailsRange) if (args.IsInDetailsRange)
@@ -275,17 +287,6 @@ public sealed partial class SleepingSystem : EntitySystem
TrySleeping(args.Target); TrySleeping(args.Target);
} }
private void Wake(Entity<SleepingComponent> ent)
{
RemComp<SleepingComponent>(ent);
_actionsSystem.RemoveAction(ent.Owner, ent.Comp.WakeAction);
var ev = new SleepStateChangedEvent(false);
RaiseLocalEvent(ent, ref ev);
_blindableSystem.UpdateIsBlind(ent.Owner);
}
/// <summary> /// <summary>
/// Try sleeping. Only mobs can sleep. /// Try sleeping. Only mobs can sleep.
/// </summary> /// </summary>
@@ -345,8 +346,7 @@ public sealed partial class SleepingSystem : EntitySystem
_popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user); _popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user);
} }
Wake((ent, ent.Comp)); return RemComp<SleepingComponent>(ent);
return true;
} }
/// <summary> /// <summary>

View File

@@ -1,12 +1,13 @@
using Content.Server.Body.Systems; using Content.Shared.Body.Systems;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server.Body.Components; namespace Content.Shared.Body.Components;
[RegisterComponent, Access(typeof(LungSystem))] [RegisterComponent, NetworkedComponent, Access(typeof(LungSystem))]
public sealed partial class LungComponent : Component public sealed partial class LungComponent : Component
{ {
[DataField] [DataField]

View File

@@ -1,19 +1,18 @@
using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos.Components;
using Content.Server.Body.Components; using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Clothing; using Content.Shared.Clothing;
using Content.Shared.Inventory.Events; using Content.Shared.Inventory.Events;
using BreathToolComponent = Content.Shared.Atmos.Components.BreathToolComponent;
using InternalsComponent = Content.Shared.Body.Components.InternalsComponent;
namespace Content.Server.Body.Systems; namespace Content.Shared.Body.Systems;
public sealed class LungSystem : EntitySystem public sealed class LungSystem : EntitySystem
{ {
[Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly SharedAtmosphereSystem _atmos = default!;
[Dependency] private readonly InternalsSystem _internals = default!; [Dependency] private readonly SharedInternalsSystem _internals = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
public static string LungSolutionName = "Lung"; public static string LungSolutionName = "Lung";

View File

@@ -3,6 +3,7 @@ using Content.Shared.Inventory;
using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Clothing.Components;
namespace Content.Shared.Eye.Blinding.Systems namespace Content.Shared.Eye.Blinding.Systems
{ {
@@ -29,6 +30,9 @@ namespace Content.Shared.Eye.Blinding.Systems
private void OnGetProtection(EntityUid uid, EyeProtectionComponent component, GetEyeProtectionEvent args) private void OnGetProtection(EntityUid uid, EyeProtectionComponent component, GetEyeProtectionEvent args)
{ {
if (TryComp<MaskComponent>(uid, out var mask) && mask.IsToggled)
return;
args.Protection += component.ProtectionTime; args.Protection += component.ProtectionTime;
} }

View File

@@ -22,6 +22,7 @@ using Robust.Shared.Timing;
using System.Linq; using System.Linq;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Random.Helpers; using Content.Shared.Random.Helpers;
using Content.Shared.Clothing.Components;
namespace Content.Shared.Flash; namespace Content.Shared.Flash;
@@ -258,6 +259,9 @@ public abstract class SharedFlashSystem : EntitySystem
private void OnFlashImmunityFlashAttempt(Entity<FlashImmunityComponent> ent, ref FlashAttemptEvent args) private void OnFlashImmunityFlashAttempt(Entity<FlashImmunityComponent> ent, ref FlashAttemptEvent args)
{ {
if (TryComp<MaskComponent>(ent, out var mask) && mask.IsToggled)
return;
if (ent.Comp.Enabled) if (ent.Comp.Enabled)
args.Cancelled = true; args.Cancelled = true;
} }

View File

@@ -1,8 +1,4 @@
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.PAI; namespace Content.Shared.PAI;
@@ -16,7 +12,7 @@ namespace Content.Shared.PAI;
/// and there's not always enough players and ghost roles to justify it. /// and there's not always enough players and ghost roles to justify it.
/// All logic in PAISystem. /// All logic in PAISystem.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent]
public sealed partial class PAIComponent : Component public sealed partial class PAIComponent : Component
{ {
/// <summary> /// <summary>
@@ -26,12 +22,6 @@ public sealed partial class PAIComponent : Component
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public EntityUid? LastUser; public EntityUid? LastUser;
[DataField]
public EntProtoId ShopActionId = "ActionPAIOpenShop";
[DataField, AutoNetworkedField]
public EntityUid? ShopAction;
/// <summary> /// <summary>
/// When microwaved there is this chance to brick the pai, kicking out its player and preventing it from being used again. /// When microwaved there is this chance to brick the pai, kicking out its player and preventing it from being used again.
/// </summary> /// </summary>

View File

@@ -1,38 +0,0 @@
using Content.Shared.Actions;
namespace Content.Shared.PAI;
/// <summary>
/// pAIs, or Personal AIs, are essentially portable ghost role generators.
/// In their current implementation, they create a ghost role anyone can access,
/// and that a player can also "wipe" (reset/kick out player).
/// Theoretically speaking pAIs are supposed to use a dedicated "offer and select" system,
/// with the player holding the pAI being able to choose one of the ghosts in the round.
/// This seems too complicated for an initial implementation, though,
/// and there's not always enough players and ghost roles to justify it.
/// </summary>
public abstract class SharedPAISystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actions = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PAIComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<PAIComponent, ComponentShutdown>(OnShutdown);
}
private void OnMapInit(Entity<PAIComponent> ent, ref MapInitEvent args)
{
_actions.AddAction(ent, ent.Comp.ShopActionId);
}
private void OnShutdown(Entity<PAIComponent> ent, ref ComponentShutdown args)
{
_actions.RemoveAction(ent.Owner, ent.Comp.ShopAction);
}
}
public sealed partial class PAIShopActionEvent : InstantActionEvent
{
}

View File

@@ -51,7 +51,6 @@ public sealed partial class ParcelWrappingSystem : EntitySystem
wrapper.Owner != target && wrapper.Owner != target &&
// Wrapper should never be empty, but may as well make sure. // Wrapper should never be empty, but may as well make sure.
!_charges.IsEmpty(wrapper.Owner) && !_charges.IsEmpty(wrapper.Owner) &&
_whitelist.IsWhitelistPass(wrapper.Comp.Whitelist, target) && _whitelist.CheckBoth(target, wrapper.Comp.Blacklist, wrapper.Comp.Whitelist);
_whitelist.IsBlacklistFail(wrapper.Comp.Blacklist, target);
} }
} }

View File

@@ -215,8 +215,6 @@ public sealed partial class RevenantComponent : Component
public string HarvestingState = "harvesting"; public string HarvestingState = "harvesting";
#endregion #endregion
[DataField] public EntityUid? Action;
/// <summary> /// <summary>
/// Offbrand - how much cold damage to deal on harvest /// Offbrand - how much cold damage to deal on harvest
/// </summary> /// </summary>

View File

@@ -42,10 +42,6 @@ public sealed class HarvestDoAfterCancelled : EntityEventArgs
{ {
} }
public sealed partial class RevenantShopActionEvent : InstantActionEvent
{
}
public sealed partial class RevenantDefileActionEvent : InstantActionEvent public sealed partial class RevenantDefileActionEvent : InstantActionEvent
{ {
} }

View File

@@ -0,0 +1,11 @@
using Content.Shared.Actions;
namespace Content.Shared.Store.Events;
/// <summary>
/// Opens a store specified by <see cref="StoreComponent"/>
/// Used for entities with a store built into themselves like Revenant or PAI
/// </summary>
public sealed partial class IntrinsicStoreActionEvent : InstantActionEvent
{
}

View File

@@ -2,7 +2,6 @@
using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Robust.Shared.Network;
namespace Content.Shared.Traits.Assorted; namespace Content.Shared.Traits.Assorted;
@@ -38,18 +37,23 @@ public sealed class PermanentBlindnessSystem : EntitySystem
{ {
_blinding.SetMinDamage((blindness.Owner, blindable), 0); _blinding.SetMinDamage((blindness.Owner, blindable), 0);
} }
// Heal all eye damage when the component is removed.
// Otherwise you would still be blind, but not *permanently* blind, meaning you have to heal the eye damage with oculine.
// This is needed for changelings that transform from a blind player to a non-blind one.
_blinding.AdjustEyeDamage((blindness.Owner, blindable), -blindable.EyeDamage);
} }
private void OnMapInit(Entity<PermanentBlindnessComponent> blindness, ref MapInitEvent args) private void OnMapInit(Entity<PermanentBlindnessComponent> blindness, ref MapInitEvent args)
{ {
if(!TryComp<BlindableComponent>(blindness.Owner, out var blindable)) if (!TryComp<BlindableComponent>(blindness.Owner, out var blindable))
return; return;
if (blindness.Comp.Blindness != 0) if (blindness.Comp.Blindness != 0)
_blinding.SetMinDamage((blindness.Owner, blindable), blindness.Comp.Blindness); _blinding.SetMinDamage((blindness.Owner, blindable), blindness.Comp.Blindness);
else else
{ {
var maxMagnitudeInt = (int) BlurryVisionComponent.MaxMagnitude; var maxMagnitudeInt = (int)BlurryVisionComponent.MaxMagnitude;
_blinding.SetMinDamage((blindness.Owner, blindable), maxMagnitudeInt); _blinding.SetMinDamage((blindness.Owner, blindable), maxMagnitudeInt);
} }
} }

View File

@@ -1,29 +1,4 @@
Entries: Entries:
- author: EmoGarbage404
changes:
- message: Added cargo orders for gold and silver ingots.
type: Add
- message: Reduced prices of cardboard and paper material crate.
type: Tweak
id: 8557
time: '2025-05-22T08:42:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/37713
- author: ArtisticRoomba
changes:
- message: The volume of large gas canisters have been increased to 1500L to encourage
their usage in resolving pressure problems in spaced rooms. The gas that starts
inside of the tanks and the price of them has increased to compensate.
type: Tweak
id: 8558
time: '2025-05-22T18:12:25.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/37564
- author: Hitlinemoss
changes:
- message: Liquid soap is now slippery.
type: Fix
id: 8559
time: '2025-05-23T21:57:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/37747
- author: CoconutThunder - author: CoconutThunder
changes: changes:
- message: The Chief Medical Officer should now appear with the correct precedence - message: The Chief Medical Officer should now appear with the correct precedence
@@ -3952,3 +3927,25 @@
id: 9059 id: 9059
time: '2025-10-08T23:45:56.0000000+00:00' time: '2025-10-08T23:45:56.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40786 url: https://github.com/space-wizards/space-station-14/pull/40786
- author: SlamBamActionman
changes:
- message: Nocturine now slows the target down, with a longer duration and shorter
delay before activating.
type: Tweak
id: 9060
time: '2025-10-09T13:32:34.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40797
- author: Princess-Cheeseballs
changes:
- message: Dying while asleep shouldn't permanently blind you anymore.
type: Fix
id: 9061
time: '2025-10-09T13:46:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40366
- author: kontakt
changes:
- message: Bulldog magazines are now only accessible through emagged fabricators.
type: Tweak
id: 9062
time: '2025-10-09T14:00:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40790

View File

@@ -764,4 +764,11 @@
id: 92 id: 92
time: '2025-10-08T20:41:46.0000000+00:00' time: '2025-10-08T20:41:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40785 url: https://github.com/space-wizards/space-station-14/pull/40785
- author: ToastEnjoyer
changes:
- message: On Plasma, added more nitrogen canisters to the maintenance hallways
type: Add
id: 93
time: '2025-10-10T01:11:33.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40794
Order: 1 Order: 1

View File

@@ -3,6 +3,8 @@ materials-glass = glass
materials-reinforced-glass = reinforced glass materials-reinforced-glass = reinforced glass
materials-plasma-glass = plasma glass materials-plasma-glass = plasma glass
materials-reinforced-plasma-glass = reinforced plasma glass materials-reinforced-plasma-glass = reinforced plasma glass
materials-uranium-glass = uranium glass
materials-reinforced-uranium-glass = reinforced uranium glass
# Metals # Metals
materials-steel = steel materials-steel = steel

View File

@@ -91,7 +91,7 @@ uplink-pistol-magazine-name = Pistol Magazine (.35 auto)
uplink-pistol-magazine-desc = Pistol magazine with 10 cartridges. Compatible with the Viper. uplink-pistol-magazine-desc = Pistol magazine with 10 cartridges. Compatible with the Viper.
uplink-pistol-magazine-c20r-name = SMG magazine (.35 auto) uplink-pistol-magazine-c20r-name = SMG magazine (.35 auto)
uplink-pistol-magazine-c20r-desc = Rifle magazine with 30 cartridges. Compatible with C-20r. uplink-pistol-magazine-c20r-desc = SMG magazine with 30 cartridges. Compatible with C-20r.
uplink-magazine-bulldog-pellet-name = Drum magazine (.50 pellet) uplink-magazine-bulldog-pellet-name = Drum magazine (.50 pellet)
uplink-magazine-bulldog-pellet-desc = Shotgun magazine with 8 shells filled with buckshot. Compatible with the Bulldog. uplink-magazine-bulldog-pellet-desc = Shotgun magazine with 8 shells filled with buckshot. Compatible with the Bulldog.

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,8 @@
- type: entity - type: entity
parent: BaseAction parent: ActionIntrinsicStore
id: ActionRevenantShop id: ActionRevenantShop
name: Shop name: Shop
description: Opens the ability shop. description: Opens the ability shop.
components:
- type: Action
icon: Interface/Actions/shop.png
- type: InstantAction
event: !type:RevenantShopActionEvent
- type: entity - type: entity
parent: BaseAction parent: BaseAction

View File

@@ -466,3 +466,14 @@
itemIconStyle: BigAction itemIconStyle: BigAction
- type: InstantAction - type: InstantAction
event: !type:ChameleonControllerOpenMenuEvent event: !type:ChameleonControllerOpenMenuEvent
- type: entity
parent: BaseMentalAction
id: ActionIntrinsicStore
name: Store
description: Opens the store
components:
- type: Action
icon: Interface/Actions/shop.png
- type: InstantAction
event: !type:IntrinsicStoreActionEvent

View File

@@ -329,6 +329,7 @@
weight: 95 weight: 95
children: children:
- id: Machete - id: Machete
- id: Claymore
- id: BaseBallBat - id: BaseBallBat
- id: CombatKnife - id: CombatKnife
- id: Spear - id: Spear

View File

@@ -66,6 +66,9 @@
type: StoreBoundUserInterface type: StoreBoundUserInterface
- type: Visibility - type: Visibility
layer: 2 #ghost vis layer layer: 2 #ghost vis layer
- type: ActionGrant
actions:
- ActionRevenantShop
- type: Store - type: Store
categories: categories:
- RevenantAbilities - RevenantAbilities

View File

@@ -105,6 +105,8 @@
- id: Ointment - id: Ointment
- id: Gauze - id: Gauze
- id: ChemistryBottleParacetamol # Offbrand - id: ChemistryBottleParacetamol # Offbrand
- id: SyndicateBusinessCard
weight: 0.5
# Packages # Packages
# TODO: Currently mostly maints loot, should be updated in the future. # TODO: Currently mostly maints loot, should be updated in the future.
@@ -138,6 +140,9 @@
weight: 0.2 weight: 0.2
rolls: !type:RangeNumberSelector rolls: !type:RangeNumberSelector
range: 2, 3 range: 2, 3
- !type:NestedSelector
tableId: InsulsTable #Uncommon since it's weighted towards budget/fingerless
weight: 0.2
- type: entityTable # TODO: Add more variety! - type: entityTable # TODO: Add more variety!
id: PackageRareEntityTable id: PackageRareEntityTable
@@ -157,3 +162,4 @@
- !type:NestedSelector - !type:NestedSelector
tableId: SyndieMaintLoot tableId: SyndieMaintLoot
weight: 0.5 weight: 0.5

View File

@@ -58,6 +58,9 @@
- Common - Common
- type: DoAfter - type: DoAfter
- type: Actions - type: Actions
- type: ActionGrant
actions:
- ActionPAIOpenShop
- type: Store - type: Store
categories: categories:
- PAIAbilities - PAIAbilities
@@ -188,15 +191,10 @@
node: potatoai node: potatoai
- type: entity - type: entity
parent: BaseMentalAction parent: ActionIntrinsicStore
id: ActionPAIOpenShop id: ActionPAIOpenShop
name: Software Catalog name: Software Catalog
description: Install new software to assist your owner. description: Install new software to assist your owner.
components:
- type: Action
icon: Interface/Actions/shop.png
- type: InstantAction
event: !type:PAIShopActionEvent
- type: entity - type: entity
parent: BaseMentalAction parent: BaseMentalAction

View File

@@ -28,6 +28,17 @@
- type: LimitedCharges - type: LimitedCharges
maxCharges: 30 maxCharges: 30
- type: entity
parent: ParcelWrap
id: ParcelWrapAdmeme
name: bluespace wrap
suffix: Admeme
description: Paper used contain items for transport. This one seems to be able to store an unusual amount of space within it.
components:
- type: ParcelWrap
whitelist: null
blacklist: null
- type: entity - type: entity
parent: BaseItem parent: BaseItem
id: WrappedParcel id: WrappedParcel

View File

@@ -136,6 +136,7 @@
- type: EmagLatheRecipes - type: EmagLatheRecipes
emagStaticPacks: emagStaticPacks:
- SecurityAmmoStatic - SecurityAmmoStatic
- SyndicateAmmoStatic
- type: BlueprintReceiver - type: BlueprintReceiver
whitelist: whitelist:
tags: tags:
@@ -408,6 +409,9 @@
- SecurityAmmo - SecurityAmmo
- SecurityWeapons - SecurityWeapons
- SecurityDisablers - SecurityDisablers
- type: EmagLatheRecipes
emagStaticPacks:
- SyndicateAmmoStatic
- type: MaterialStorage - type: MaterialStorage
whitelist: whitelist:
tags: tags:
@@ -444,6 +448,9 @@
runningState: icon runningState: icon
staticPacks: staticPacks:
- SecurityAmmoStatic - SecurityAmmoStatic
- type: EmagLatheRecipes
emagStaticPacks:
- SyndicateAmmoStatic
- type: MaterialStorage - type: MaterialStorage
whitelist: whitelist:
tags: tags:

View File

@@ -306,16 +306,19 @@
Narcotic: Narcotic:
statusEffects: # Offbrand statusEffects: # Offbrand
- statusEffect: StatusEffectPainSuppressionNocturine - statusEffect: StatusEffectPainSuppressionNocturine
effects: effects: # It would be nice to have speech slurred or mumbly, but accents are a bit iffy atm. Same for distortion effects.
- !type:MovespeedModifier
walkSpeedModifier: 0.65
sprintSpeedModifier: 0.65
- !type:ModifyStatusEffect - !type:ModifyStatusEffect
conditions: conditions:
- !type:ReagentThreshold - !type:ReagentThreshold
reagent: Nocturine reagent: Nocturine
min: 8 min: 8
effectProto: StatusEffectForcedSleeping effectProto: StatusEffectForcedSleeping
time: 3 time: 6
delay: 6 delay: 5
type: Add type: Update
- type: reagent - type: reagent
id: MuteToxin id: MuteToxin

View File

@@ -49,9 +49,6 @@
- MagazinePistolSubMachineGunTopMountedEmpty - MagazinePistolSubMachineGunTopMountedEmpty
- MagazineRifle - MagazineRifle
- MagazineRifleEmpty - MagazineRifleEmpty
- MagazineShotgun
- MagazineShotgunEmpty
- MagazineShotgunSlug
- SpeedLoaderMagnum - SpeedLoaderMagnum
- SpeedLoaderMagnumEmpty - SpeedLoaderMagnumEmpty

View File

@@ -0,0 +1,9 @@
## Static recipes
# Added to emagged autolathe
- type: latheRecipePack
id: SyndicateAmmoStatic
recipes:
- MagazineShotgun
- MagazineShotgunEmpty
- MagazineShotgunSlug