From 0396f8eb9dcb390ccaeeb3d1bef5c68685fc8794 Mon Sep 17 00:00:00 2001
From: Flipp Syder <76629141+vulppine@users.noreply.github.com>
Date: Tue, 16 Aug 2022 02:57:39 -0700
Subject: [PATCH 001/109] Adds some extra toys to the arcade machine (#10637)
---
.../Components/SpaceVillainArcadeComponent.cs | 8 +++-
.../Components/EnergySwordComponent.cs | 9 ++--
.../Melee/EnergySword/EnergySwordSystem.cs | 6 ++-
.../Systems/SharedGunSystem.Ballistic.cs | 10 ++--
.../Prototypes/Entities/Objects/Fun/toys.yml | 48 ++++++++++++++++++-
.../Weapons/Guns/Ammunition/Boxes/toy.yml | 2 +-
.../Guns/Ammunition/Projectiles/toy.yml | 40 ++++------------
.../Weapons/Guns/Projectiles/projectiles.yml | 18 -------
Resources/Prototypes/tags.yml | 5 +-
9 files changed, 85 insertions(+), 61 deletions(-)
diff --git a/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs b/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs
index fca97c2757..48fad3d2d0 100644
--- a/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs
+++ b/Content.Server/Arcade/Components/SpaceVillainArcadeComponent.cs
@@ -8,6 +8,8 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+// TODO: ECS.
+
namespace Content.Server.Arcade.Components
{
[RegisterComponent]
@@ -54,7 +56,11 @@ namespace Content.Server.Arcade.Components
{
"ToyMouse", "ToyAi", "ToyNuke", "ToyAssistant", "ToyGriffin", "ToyHonk", "ToyIan",
"ToyMarauder", "ToyMauler", "ToyGygax", "ToyOdysseus", "ToyOwlman", "ToyDeathRipley",
- "ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton"
+ "ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton",
+ "FoamCrossbow", "RevolverCapGun", "PlushieLizard", "PlushieSpaceLizard",
+ "PlushieNuke", "PlushieCarp", "PlushieRatvar", "PlushieNar", "PlushieSnake", "Basketball", "Football",
+ "PlushieRouny", "PlushieBee", "PlushieSlime", "BalloonCorgi", "ToySword", "CrayonBox", "BoxDonkSoftBox", "BoxCartridgeCap",
+ "HarmonicaInstrument", "OcarinaInstrument", "RecorderInstrument", "GunpetInstrument", "BirdToyInstrument"
};
protected override void Initialize()
diff --git a/Content.Server/Weapon/Melee/EnergySword/Components/EnergySwordComponent.cs b/Content.Server/Weapon/Melee/EnergySword/Components/EnergySwordComponent.cs
index f7fac0bc8d..532a2b206e 100644
--- a/Content.Server/Weapon/Melee/EnergySword/Components/EnergySwordComponent.cs
+++ b/Content.Server/Weapon/Melee/EnergySword/Components/EnergySwordComponent.cs
@@ -12,6 +12,9 @@ namespace Content.Server.Weapon.Melee.EnergySword
public bool Activated = false;
+ [DataField("isSharp")]
+ public bool IsSharp = true;
+
///
/// RGB cycle rate for hacked e-swords.
///
@@ -40,10 +43,10 @@ namespace Content.Server.Weapon.Melee.EnergySword
Color.MediumOrchid
};
- [DataField("litDamageBonus", required: true)]
- public DamageSpecifier LitDamageBonus = default!;
+ [DataField("litDamageBonus")]
+ public DamageSpecifier LitDamageBonus = new();
- [DataField("litDisarmMalus", required: true)]
+ [DataField("litDisarmMalus")]
public float litDisarmMalus = 0.6f;
}
}
diff --git a/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs b/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs
index a94afbb90c..25bd429aa7 100644
--- a/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs
+++ b/Content.Server/Weapon/Melee/EnergySword/EnergySwordSystem.cs
@@ -80,7 +80,8 @@ namespace Content.Server.Weapon.Melee.EnergySword
if(TryComp(comp.Owner, out var weaponComp))
weaponComp.HitSound = comp.OnHitOff;
- RemComp(comp.Owner);
+ if (comp.IsSharp)
+ RemComp(comp.Owner);
SoundSystem.Play(comp.DeActivateSound.GetSound(), Filter.Pvs(comp.Owner, entityManager: EntityManager), comp.Owner);
@@ -97,7 +98,8 @@ namespace Content.Server.Weapon.Melee.EnergySword
_item.SetSize(comp.Owner, 9999, item);
}
- EnsureComp(comp.Owner);
+ if (comp.IsSharp)
+ EnsureComp(comp.Owner);
if(TryComp(comp.Owner, out var weaponComp))
weaponComp.HitSound = comp.OnHitOn;
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
index b67af7514c..56d89a048e 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
@@ -151,15 +151,17 @@ public abstract partial class SharedGunSystem
{
entity = component.Entities[^1];
+ args.Ammo.Add(EnsureComp(entity));
+
// Leave the entity as is if it doesn't auto cycle
// TODO: Suss this out with NewAmmoComponent as I don't think it gets removed from container properly
- if (HasComp(entity) && component.AutoCycle)
+ if (!component.AutoCycle)
{
- component.Entities.RemoveAt(component.Entities.Count - 1);
- component.Container.Remove(entity);
+ return;
}
- args.Ammo.Add(EnsureComp(entity));
+ component.Entities.RemoveAt(component.Entities.Count - 1);
+ component.Container.Remove(entity);
}
else if (component.UnspawnedCount > 0)
{
diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml
index 128b541a6f..d1ea5e30c1 100644
--- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml
+++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml
@@ -487,7 +487,7 @@
- type: BallisticAmmoProvider
whitelist:
tags:
- - CartridgeRocket
+ - BulletFoam
capacity: 1
soundInsert:
path: /Audio/Weapons/drawbow2.ogg
@@ -648,3 +648,49 @@
size: 24
sprite: Objects/Fun/toys.rsi
heldPrefix: orb
+
+- type: entity
+ id: ToySword
+ name: toy sword
+ description: New Sandy-Cat plastic sword! Comes with realistic sound and full color! Looks almost like the real thing!
+ components:
+ - type: EnergySword
+ isSharp: false
+ colorOptions:
+ - DodgerBlue
+ - type: Sprite
+ sprite: Objects/Weapons/Melee/e_sword.rsi
+ layers:
+ - state: e_sword
+ - state: e_sword_blade
+ color: "#FFFFFF"
+ visible: false
+ shader: unshaded
+ map: [ "blade" ]
+ - type: Item
+ size: 5
+ sprite: Objects/Weapons/Melee/e_sword.rsi
+ - type: UseDelay
+ delay: 1.0
+ - type: PointLight
+ netsync: false
+ enabled: false
+ radius: 2
+ energy: 2
+ color: white
+ - type: Appearance
+ - type: ToggleableLightVisuals
+ spriteLayer: blade
+ inhandVisuals:
+ left:
+ - state: inhand-left-blade
+ shader: unshaded
+ right:
+ - state: inhand-right-blade
+ shader: unshaded
+ - type: DisarmMalus
+ malus: 0
+ - type: MeleeWeapon
+ damage:
+ types:
+ Blunt: 0
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
index 62098078ad..39904580fa 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
@@ -20,7 +20,7 @@
components:
- type: BallisticAmmoProvider
capacity: 40
- proto: BulletDonkSoft
+ proto: BulletFoam
- type: Sprite
sprite: Objects/Fun/toys.rsi
state: foambox
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml
index 6ded681011..3909d4c28d 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml
@@ -1,34 +1,14 @@
- type: entity
- id: BaseBulletDonkSoft
+ id: BulletFoam
+ description: I hope you're wearing eye protection.
name: foam dart
parent: BaseItem
- abstract: true
components:
- - type: Tag
- tags:
- - Cartridge
- - CartridgeRocket
- - type: Projectile
- damage:
- types:
- Blunt: 1
- soundHit:
- path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg
-
-- type: entity
- id: BulletDonkSoft
- name: foam dart
- parent: BaseBulletDonkSoft
- description: I hope you're wearing eye protection.
- components:
- - type: CartridgeAmmo
- proto: BulletFoam
- deleteOnSpawn: true
- - type: Sprite
- sprite: Objects/Fun/toys.rsi
- layers:
- - state: foamdart
- - type: Projectile
- damage:
- types:
- Blunt: 1
+ - type: Tag
+ tags:
+ - BulletFoam
+ - type: Ammo
+ - type: Sprite
+ sprite: Objects/Fun/toys.rsi
+ layers:
+ - state: foamdart
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
index c3c4c4b827..fc26a02171 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
@@ -323,24 +323,6 @@
intensitySlope: 1
maxIntensity: 10
-- type: entity
- id: BulletFoam
- name: foam dart
- parent: BaseBullet
- noSpawn: true
- components:
- - type: Sprite
- sprite: Objects/Fun/toys.rsi
- layers:
- - state: foamdart
- - type: Projectile
- deleteOnCollide: true
- damage:
- types:
- Blunt: 2
- soundHit:
- path: /Audio/Weapons/Guns/Hits/snap.ogg
-
- type: entity
id: BulletCap
name: cap bullet
diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml
index c012ad82ab..97768a6690 100644
--- a/Resources/Prototypes/tags.yml
+++ b/Resources/Prototypes/tags.yml
@@ -36,6 +36,9 @@
- type: Tag
id: Bottle
+- type: Tag
+ id: BulletFoam
+
- type: Tag
id: CableCoil
@@ -307,7 +310,7 @@
- type: Tag
id: MailingUnitElectronics
-
+
- type: Tag
id: Medkit
From 34bcbc95004242d7a2af4b33bc1c40bfd4611781 Mon Sep 17 00:00:00 2001
From: PJBot
Date: Tue, 16 Aug 2022 05:58:43 -0400
Subject: [PATCH 002/109] Automatic changelog update
---
Resources/Changelog/Changelog.yml | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index 0c523ff7d1..a82e15ba29 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,9 +1,4 @@
Entries:
-- author: metalgearsloth
- changes:
- - {message: Anchoring now shows popups, type: Add}
- id: 1662
- time: '2022-06-05T23:04:40.0000000+00:00'
- author: WilliamECrew
changes:
- {message: 'When dumping contents out of a bag, items will now be spread out randomly
@@ -2932,3 +2927,10 @@ Entries:
- {message: Removed the ability to point while asleep, type: Fix}
id: 2161
time: '2022-08-16T04:22:17.0000000+00:00'
+- author: vulppine
+ changes:
+ - {message: "FunTime Arcade Solutions\u2122 has outfitted NanoTrasen stations with\
+ \ a new supply of arcade toys! Come spend your free time at the arcade machines\
+ \ for exciting, fun objects!", type: Add}
+ id: 2162
+ time: '2022-08-16T09:57:40.0000000+00:00'
From 3c3a44ec49318ca9408e50d2e2b91c3627fafdeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20K=C3=A4mper?=
Date: Tue, 16 Aug 2022 12:04:07 +0200
Subject: [PATCH 003/109] Port wires/maintenance panel visualizer (#10543)
---
Content.Client/Doors/AirlockVisualizer.cs | 4 +-
Content.Client/Lathe/LatheSystem.cs | 8 --
.../Wires/Visualizers/WiresVisualizer.cs | 31 -------
.../Visualizers/WiresVisualizerSystem.cs | 32 +++++++
Content.Client/Wires/WiresVisualsComponent.cs | 7 ++
.../Instruments/instruments_structures.yml | 3 +-
.../Doors/Airlocks/base_structureairlocks.yml | 2 +-
.../Structures/Doors/Airlocks/external.yml | 2 +-
.../Structures/Doors/Airlocks/highsec.yml | 2 +-
.../Structures/Doors/Firelocks/firelock.yml | 2 +-
.../Doors/Windoors/base_structurewindoors.yml | 2 +-
.../Entities/Structures/Machines/lathe.yml | 2 +
.../Structures/Machines/vending_machines.yml | 86 +++++++++----------
.../Structures/Wallmounts/air_alarm.yml | 2 +-
.../Structures/Wallmounts/fire_alarm.yml | 2 +-
15 files changed, 94 insertions(+), 93 deletions(-)
delete mode 100644 Content.Client/Wires/Visualizers/WiresVisualizer.cs
create mode 100644 Content.Client/Wires/Visualizers/WiresVisualizerSystem.cs
create mode 100644 Content.Client/Wires/WiresVisualsComponent.cs
diff --git a/Content.Client/Doors/AirlockVisualizer.cs b/Content.Client/Doors/AirlockVisualizer.cs
index 6769568f71..e2ff79362a 100644
--- a/Content.Client/Doors/AirlockVisualizer.cs
+++ b/Content.Client/Doors/AirlockVisualizer.cs
@@ -85,7 +85,7 @@ namespace Content.Client.Doors
{
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
- flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
+ flickMaintenancePanel.LayerKey = WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
}
}
@@ -109,7 +109,7 @@ namespace Content.Client.Doors
{
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel);
- flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
+ flickMaintenancePanel.LayerKey = WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f));
}
}
diff --git a/Content.Client/Lathe/LatheSystem.cs b/Content.Client/Lathe/LatheSystem.cs
index 4f7f9ad485..514c851fac 100644
--- a/Content.Client/Lathe/LatheSystem.cs
+++ b/Content.Client/Lathe/LatheSystem.cs
@@ -2,8 +2,6 @@ using Robust.Client.GameObjects;
using Content.Shared.Lathe;
using Content.Shared.Power;
using Content.Client.Power;
-using Content.Client.Wires.Visualizers;
-using Content.Shared.Wires;
namespace Content.Client.Lathe
{
@@ -20,12 +18,6 @@ namespace Content.Client.Lathe
args.Sprite.LayerSetVisible(PowerDeviceVisualLayers.Powered, powered);
}
- if (args.Component.TryGetData(WiresVisuals.MaintenancePanelState, out bool panel)
- && args.Sprite.LayerMapTryGet(WiresVisualizer.WiresVisualLayers.MaintenancePanel, out _))
- {
- args.Sprite.LayerSetVisible(WiresVisualizer.WiresVisualLayers.MaintenancePanel, panel);
- }
-
// Lathe specific stuff
if (args.Component.TryGetData(LatheVisuals.IsRunning, out bool isRunning))
{
diff --git a/Content.Client/Wires/Visualizers/WiresVisualizer.cs b/Content.Client/Wires/Visualizers/WiresVisualizer.cs
deleted file mode 100644
index 0431735daf..0000000000
--- a/Content.Client/Wires/Visualizers/WiresVisualizer.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Content.Shared.Wires;
-using Robust.Client.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-
-namespace Content.Client.Wires.Visualizers
-{
- public sealed class WiresVisualizer : AppearanceVisualizer
- {
- [Obsolete("Subscribe to AppearanceChangeEvent instead.")]
- public override void OnChangeData(AppearanceComponent component)
- {
- base.OnChangeData(component);
- var sprite = IoCManager.Resolve().GetComponent(component.Owner);
- if (component.TryGetData(WiresVisuals.MaintenancePanelState, out var state))
- {
- sprite.LayerSetVisible(WiresVisualLayers.MaintenancePanel, state);
- }
- // Mainly for spawn window
- else
- {
- sprite.LayerSetVisible(WiresVisualLayers.MaintenancePanel, false);
- }
- }
-
- public enum WiresVisualLayers : byte
- {
- MaintenancePanel,
- }
- }
-}
diff --git a/Content.Client/Wires/Visualizers/WiresVisualizerSystem.cs b/Content.Client/Wires/Visualizers/WiresVisualizerSystem.cs
new file mode 100644
index 0000000000..ffc8b09a1a
--- /dev/null
+++ b/Content.Client/Wires/Visualizers/WiresVisualizerSystem.cs
@@ -0,0 +1,32 @@
+using Content.Shared.Wires;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Wires.Visualizers
+{
+ public sealed class WiresVisualizerSystem : VisualizerSystem
+ {
+ protected override void OnAppearanceChange(EntityUid uid, WiresVisualsComponent component, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+
+ var layer = args.Sprite.LayerMapReserveBlank(WiresVisualLayers.MaintenancePanel);
+
+ if(args.AppearanceData.TryGetValue(WiresVisuals.MaintenancePanelState, out var panelStateObject) &&
+ panelStateObject is bool panelState)
+ {
+ args.Sprite.LayerSetVisible(layer, panelState);
+ }
+ else
+ {
+ //Mainly for spawn window
+ args.Sprite.LayerSetVisible(layer, false);
+ }
+ }
+ }
+
+ public enum WiresVisualLayers : byte
+ {
+ MaintenancePanel
+ }
+}
diff --git a/Content.Client/Wires/WiresVisualsComponent.cs b/Content.Client/Wires/WiresVisualsComponent.cs
new file mode 100644
index 0000000000..f2c7e47740
--- /dev/null
+++ b/Content.Client/Wires/WiresVisualsComponent.cs
@@ -0,0 +1,7 @@
+namespace Content.Client.Wires
+{
+ [RegisterComponent]
+ public sealed class WiresVisualsComponent : Component
+ {
+ }
+}
diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
index 5296e7f346..3f35f6945a 100644
--- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
+++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
@@ -160,8 +160,7 @@
- state: daw-panel
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: Appearance
- visuals:
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: Construction
graph: Machine
node: machine
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml
index dbf6905b16..7ac55d69a6 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml
@@ -61,7 +61,7 @@
- type: Appearance
visuals:
- type: AirlockVisualizer
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: ApcPowerReceiver
powerLoad: 20
- type: ExtensionCableReceiver
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml
index dd8e261dc5..7eeddc9dd2 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml
@@ -20,7 +20,7 @@
- type: Appearance
visuals:
- type: AirlockVisualizer
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PaintableAirlock
group: External
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml
index 42f6a79afd..fa04042d57 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml
@@ -53,7 +53,7 @@
- type: Appearance
visuals:
- type: AirlockVisualizer
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: ApcPowerReceiver
powerLoad: 20
- type: ExtensionCableReceiver
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
index 3e63d5e4b2..7581e6c749 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
@@ -77,7 +77,7 @@
- type: AirlockVisualizer
animationTime: 0.6
emergencyAccessLayer: false
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: Wires
BoardName: "Firelock Control"
LayoutId: Firelock
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml
index 749c898ffe..eec19ea444 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml
@@ -101,7 +101,7 @@
denyAnimationTime: 0.4
animatedPanel: false
openUnlitVisible: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: Airtight
fixVacuum: true
noAirWhenFullyAirBlocked: false
diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
index ee968f1bc6..87e213238c 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
@@ -21,6 +21,7 @@
- type: LatheVisuals
idleState: icon
runningState: building
+ - type: WiresVisuals
- type: Physics
bodyType: Static
- type: Fixtures
@@ -107,6 +108,7 @@
- type: LatheVisuals
idleState: icon
runningState: building
+ - type: WiresVisuals
- type: Physics
bodyType: Static
- type: Fixtures
diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml
index f9f2d4f45a..ddd8eac90c 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml
@@ -95,7 +95,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: entity
parent: VendingMachine
@@ -123,7 +123,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Bar"]]
@@ -152,7 +152,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1
energy: 1.3
@@ -190,7 +190,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: entity
parent: VendingMachine
@@ -222,7 +222,7 @@
normalUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.8
energy: 1.6
@@ -262,7 +262,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.3
@@ -298,7 +298,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -330,7 +330,7 @@
normalUnshaded: true
ejectUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Service"]]
- type: PointLight
@@ -364,7 +364,7 @@
normalUnshaded: true
ejectUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -398,7 +398,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -429,7 +429,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Engineering"]]
- type: PointLight
@@ -464,7 +464,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Medical"]]
- type: PointLight
@@ -499,7 +499,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Hydroponics"]]
- type: PointLight
@@ -534,7 +534,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Security"]]
- type: PointLight
@@ -569,7 +569,7 @@
normalUnshaded: true
ejectUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -609,7 +609,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -645,7 +645,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -681,7 +681,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -719,7 +719,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -757,7 +757,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -787,7 +787,7 @@
normalUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Medical"]]
@@ -819,7 +819,7 @@
ejectUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -854,7 +854,7 @@
normalUnshaded: true
ejectUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -888,7 +888,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -920,7 +920,7 @@
ejectUnshaded: false
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -954,7 +954,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -987,7 +987,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Hydroponics"]]
@@ -1016,7 +1016,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Brig"]]
@@ -1045,7 +1045,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Security"]]
@@ -1074,7 +1074,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Bar"]]
@@ -1103,7 +1103,7 @@
normalUnshaded: true
denyUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: PointLight
radius: 1.5
energy: 1.6
@@ -1134,7 +1134,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Cargo"]]
@@ -1163,7 +1163,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Medical"]]
@@ -1192,7 +1192,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Chemistry"]]
@@ -1221,7 +1221,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Engineering"]]
@@ -1250,7 +1250,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Engineering"]]
@@ -1279,7 +1279,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Kitchen"]]
@@ -1308,7 +1308,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Security"]]
@@ -1337,7 +1337,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Janitor"]]
@@ -1366,7 +1366,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Research"]]
@@ -1395,7 +1395,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Research"]]
@@ -1424,7 +1424,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Medical"]]
@@ -1453,7 +1453,7 @@
- type: VendingMachineVisualizer
normalUnshaded: true
broken: true
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AccessReader
access: [["Medical"]]
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml
index 1b82ae3696..6d990bf5c4 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml
@@ -56,7 +56,7 @@
Danger: alarm1
setOnDepowered:
airAlarmBase: alarmp
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: Sprite
sprite: Structures/Wallmounts/air_monitors.rsi
layers:
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml
index fc6ebe558d..1a213aa0ad 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml
@@ -33,7 +33,7 @@
Danger: fire_on
Emagged: fire_emagged
hideOnDepowered: ["fireAlarmState"]
- - type: WiresVisualizer
+ - type: WiresVisuals
- type: AlertLevelDisplay
alertVisuals:
green: fire_0
From cf8ad5f8159da6246b76e87c63ca1a9e46d6e3a5 Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Tue, 16 Aug 2022 22:19:54 +1200
Subject: [PATCH 004/109] Move MultipleTool to shared (#9964)
---
.../Tools/Components/MultipleToolComponent.cs | 70 +++++---------
Content.Client/Tools/ToolSystem.cs | 35 ++++++-
.../Tools/Components/MultipleToolComponent.cs | 40 +-------
.../Tools/ToolSystem.MultipleTool.cs | 94 -------------------
Content.Server/Tools/ToolSystem.cs | 10 +-
.../Components/SharedMultipleToolComponent.cs | 32 ++++++-
.../Systems/SharedToolSystem.MultipleTool.cs | 93 ++++++++++++++++++
7 files changed, 188 insertions(+), 186 deletions(-)
delete mode 100644 Content.Server/Tools/ToolSystem.MultipleTool.cs
create mode 100644 Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs
diff --git a/Content.Client/Tools/Components/MultipleToolComponent.cs b/Content.Client/Tools/Components/MultipleToolComponent.cs
index 34030d58db..0c088c480c 100644
--- a/Content.Client/Tools/Components/MultipleToolComponent.cs
+++ b/Content.Client/Tools/Components/MultipleToolComponent.cs
@@ -1,72 +1,50 @@
-using Content.Client.Items.Components;
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Shared.Tools.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
-using Robust.Shared.GameObjects;
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
-using Robust.Shared.ViewVariables;
namespace Content.Client.Tools.Components
{
[RegisterComponent]
- public sealed class MultipleToolComponent : SharedMultipleToolComponent, IItemStatus
+ [ComponentReference(typeof(SharedMultipleToolComponent))]
+ public sealed class MultipleToolComponent : SharedMultipleToolComponent
{
- private string? _behavior;
+ [ViewVariables(VVAccess.ReadWrite)]
+ public bool UiUpdateNeeded;
+
[DataField("statusShowBehavior")]
- private bool _statusShowBehavior = true;
+ public bool StatusShowBehavior = true;
+ }
- [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
- [ViewVariables] public bool StatusShowBehavior => _statusShowBehavior;
- [ViewVariables] public string? Behavior => _behavior;
+ public sealed class MultipleToolStatusControl : Control
+ {
+ private readonly MultipleToolComponent _parent;
+ private readonly RichTextLabel _label;
- public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
+ public MultipleToolStatusControl(MultipleToolComponent parent)
{
- base.HandleComponentState(curState, nextState);
-
- if (curState is not MultipleToolComponentState tool) return;
-
- _behavior = tool.QualityName;
- _uiUpdateNeeded = true;
-
+ _parent = parent;
+ _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
+ _label.SetMarkup(_parent.StatusShowBehavior ? _parent.CurrentQualityName : string.Empty);
+ AddChild(_label);
}
- public Control MakeControl() => new StatusControl(this);
-
- private sealed class StatusControl : Control
+ protected override void FrameUpdate(FrameEventArgs args)
{
- private readonly MultipleToolComponent _parent;
- private readonly RichTextLabel _label;
+ base.FrameUpdate(args);
- public StatusControl(MultipleToolComponent parent)
+ if (_parent.UiUpdateNeeded)
{
- _parent = parent;
- _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
- AddChild(_label);
-
- UpdateDraw();
- }
-
- protected override void FrameUpdate(FrameEventArgs args)
- {
- base.FrameUpdate(args);
-
- if (!_parent._uiUpdateNeeded)
- {
- return;
- }
+ _parent.UiUpdateNeeded = false;
Update();
}
+ }
- public void Update()
- {
- _parent._uiUpdateNeeded = false;
-
- _label.SetMarkup(_parent.StatusShowBehavior ? _parent.Behavior ?? string.Empty : string.Empty);
- }
+ public void Update()
+ {
+ _label.SetMarkup(_parent.StatusShowBehavior ? _parent.CurrentQualityName : string.Empty);
}
}
}
diff --git a/Content.Client/Tools/ToolSystem.cs b/Content.Client/Tools/ToolSystem.cs
index 9f0bd5aa1a..7c59ed7d6c 100644
--- a/Content.Client/Tools/ToolSystem.cs
+++ b/Content.Client/Tools/ToolSystem.cs
@@ -1,17 +1,48 @@
+using Content.Client.Items;
using Content.Client.Tools.Components;
+using Content.Shared.Tools;
using Content.Shared.Tools.Components;
-using Robust.Shared.GameObjects;
+using Robust.Client.GameObjects;
using Robust.Shared.GameStates;
namespace Content.Client.Tools
{
- public sealed class ToolSystem : EntitySystem
+ public sealed class ToolSystem : SharedToolSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnWelderHandleState);
+ SubscribeLocalEvent(OnGetStatusMessage);
+ }
+
+ public override void SetMultipleTool(EntityUid uid,
+ SharedMultipleToolComponent? multiple = null,
+ ToolComponent? tool = null,
+ bool playSound = false,
+ EntityUid? user = null)
+ {
+ if (!Resolve(uid, ref multiple))
+ return;
+
+ base.SetMultipleTool(uid, multiple, tool, playSound, user);
+ ((MultipleToolComponent)multiple).UiUpdateNeeded = true;
+
+ // TODO replace this with appearance + visualizer
+ // in order to convert this to a specifier, the manner in which the sprite is specified in yaml needs to be updated.
+
+ if (multiple.Entries.Length > multiple.CurrentEntry && TryComp(uid, out SpriteComponent? sprite))
+ {
+ var current = multiple.Entries[multiple.CurrentEntry];
+ if (current.Sprite != null)
+ sprite.LayerSetSprite(0, current.Sprite);
+ }
+ }
+
+ private void OnGetStatusMessage(EntityUid uid, MultipleToolComponent welder, ItemStatusCollectMessage args)
+ {
+ args.Controls.Add(new MultipleToolStatusControl(welder));
}
private void OnWelderHandleState(EntityUid uid, WelderComponent welder, ref ComponentHandleState args)
diff --git a/Content.Server/Tools/Components/MultipleToolComponent.cs b/Content.Server/Tools/Components/MultipleToolComponent.cs
index eb7a383596..1bc81039d0 100644
--- a/Content.Server/Tools/Components/MultipleToolComponent.cs
+++ b/Content.Server/Tools/Components/MultipleToolComponent.cs
@@ -1,39 +1,9 @@
-using Content.Shared.Tools;
using Content.Shared.Tools.Components;
-using Robust.Shared.Audio;
-using Robust.Shared.Utility;
-namespace Content.Server.Tools.Components
+namespace Content.Server.Tools.Components;
+
+[RegisterComponent]
+[ComponentReference(typeof(SharedMultipleToolComponent))]
+public sealed class MultipleToolComponent : SharedMultipleToolComponent
{
- ///
- /// Not to be confused with Multitool (power)
- ///
- [RegisterComponent]
- public sealed class MultipleToolComponent : SharedMultipleToolComponent
- {
- [DataDefinition]
- public sealed class ToolEntry
- {
- [DataField("behavior", required:true)]
- public PrototypeFlags Behavior { get; } = new();
-
- [DataField("useSound")]
- public SoundSpecifier? Sound { get; } = null;
-
- [DataField("changeSound")]
- public SoundSpecifier? ChangeSound { get; } = null;
-
- [DataField("sprite")]
- public SpriteSpecifier? Sprite { get; } = null;
- }
-
- [DataField("entries", required:true)]
- public ToolEntry[] Entries { get; } = Array.Empty();
-
- [ViewVariables]
- public int CurrentEntry = 0;
-
- [ViewVariables]
- public string CurrentQualityName = string.Empty;
- }
}
diff --git a/Content.Server/Tools/ToolSystem.MultipleTool.cs b/Content.Server/Tools/ToolSystem.MultipleTool.cs
deleted file mode 100644
index 646f9250f2..0000000000
--- a/Content.Server/Tools/ToolSystem.MultipleTool.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using System.Linq;
-using Content.Server.Tools.Components;
-using Content.Shared.Interaction;
-using Content.Shared.Tools;
-using Content.Shared.Tools.Components;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-using Robust.Shared.Player;
-
-namespace Content.Server.Tools
-{
- public sealed partial class ToolSystem
- {
- private void InitializeMultipleTools()
- {
- SubscribeLocalEvent(OnMultipleToolStartup);
- SubscribeLocalEvent(OnMultipleToolActivated);
- SubscribeLocalEvent(OnMultipleToolGetState);
- }
-
- private void OnMultipleToolStartup(EntityUid uid, MultipleToolComponent multiple, ComponentStartup args)
- {
- // Only set the multiple tool if we have a tool component.
- if(EntityManager.TryGetComponent(uid, out ToolComponent? tool))
- SetMultipleTool(uid, multiple, tool);
- }
-
- private void OnMultipleToolActivated(EntityUid uid, MultipleToolComponent multiple, ActivateInWorldEvent args)
- {
- if (args.Handled)
- return;
-
- args.Handled = CycleMultipleTool(uid, multiple);
- }
-
- private void OnMultipleToolGetState(EntityUid uid, MultipleToolComponent multiple, ref ComponentGetState args)
- {
- args.State = new MultipleToolComponentState(multiple.CurrentQualityName);
- }
-
- public bool CycleMultipleTool(EntityUid uid, MultipleToolComponent? multiple = null)
- {
- if (!Resolve(uid, ref multiple))
- return false;
-
- if (multiple.Entries.Length == 0)
- return false;
-
- multiple.CurrentEntry = (multiple.CurrentEntry + 1) % multiple.Entries.Length;
- SetMultipleTool(uid, multiple);
-
- var current = multiple.Entries[multiple.CurrentEntry];
-
- if(current.ChangeSound is {} changeSound)
- SoundSystem.Play(changeSound.GetSound(), Filter.Pvs(uid), uid);
-
- return true;
- }
-
- public void SetMultipleTool(EntityUid uid, MultipleToolComponent? multiple = null, ToolComponent? tool = null, SpriteComponent? sprite = null)
- {
- if (!Resolve(uid, ref multiple, ref tool))
- return;
-
- // Sprite is optional.
- Resolve(uid, ref sprite, false);
-
- if (multiple.Entries.Length == 0)
- {
- multiple.CurrentQualityName = Loc.GetString("multiple-tool-component-no-behavior");
- multiple.Dirty();
- return;
- }
-
- var current = multiple.Entries[multiple.CurrentEntry];
-
- tool.UseSound = current.Sound;
- tool.Qualities = current.Behavior;
-
- if (_prototypeManager.TryIndex(current.Behavior.First(), out ToolQualityPrototype? quality))
- {
- multiple.CurrentQualityName = Loc.GetString(quality.Name);
- }
-
- multiple.Dirty();
-
- if (current.Sprite == null || sprite == null)
- return;
-
- sprite.LayerSetSprite(0, current.Sprite);
- }
- }
-}
diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs
index f4d2f82167..b890d1ebdd 100644
--- a/Content.Server/Tools/ToolSystem.cs
+++ b/Content.Server/Tools/ToolSystem.cs
@@ -1,26 +1,25 @@
-using System.Threading;
-using System.Threading.Tasks;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Popups;
using Content.Shared.Audio;
using Content.Shared.Item;
+using Content.Shared.Tools;
using Content.Shared.Tools.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
+using System.Threading;
+using System.Threading.Tasks;
namespace Content.Server.Tools
{
// TODO move tool system to shared, and make it a friend of Tool Component.
- public sealed partial class ToolSystem : EntitySystem
+ public sealed partial class ToolSystem : SharedToolSystem
{
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
@@ -34,7 +33,6 @@ namespace Content.Server.Tools
InitializeTilePrying();
InitializeWelders();
- InitializeMultipleTools();
SubscribeLocalEvent(OnDoAfterComplete);
SubscribeLocalEvent(OnDoAfterCancelled);
diff --git a/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs b/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs
index 087d1b2ce3..fd05aa3ba5 100644
--- a/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs
+++ b/Content.Shared/Tools/Components/SharedMultipleToolComponent.cs
@@ -1,21 +1,47 @@
+using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
namespace Content.Shared.Tools.Components
{
[NetworkedComponent]
public abstract class SharedMultipleToolComponent : Component
{
+ [DataDefinition]
+ public sealed class ToolEntry
+ {
+ [DataField("behavior", required: true)]
+ public PrototypeFlags Behavior { get; } = new();
+
+ [DataField("useSound")]
+ public SoundSpecifier? Sound { get; } = null;
+
+ [DataField("changeSound")]
+ public SoundSpecifier? ChangeSound { get; } = null;
+
+ [DataField("sprite")]
+ public SpriteSpecifier? Sprite { get; } = null;
+ }
+
+ [DataField("entries", required: true)]
+ public ToolEntry[] Entries { get; } = Array.Empty();
+
+ [ViewVariables]
+ public uint CurrentEntry = 0;
+
+ [ViewVariables]
+ public string CurrentQualityName = String.Empty;
}
[NetSerializable, Serializable]
public sealed class MultipleToolComponentState : ComponentState
{
- public string QualityName { get; }
+ public readonly uint Selected;
- public MultipleToolComponentState(string qualityName)
+ public MultipleToolComponentState(uint selected)
{
- QualityName = qualityName;
+ Selected = selected;
}
}
}
diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs
new file mode 100644
index 0000000000..4f71de930e
--- /dev/null
+++ b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs
@@ -0,0 +1,93 @@
+using System.Linq;
+using Content.Shared.Interaction;
+using Content.Shared.Tools.Components;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Tools;
+
+public abstract class SharedToolSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+ [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnMultipleToolStartup);
+ SubscribeLocalEvent(OnMultipleToolActivated);
+ SubscribeLocalEvent(OnMultipleToolGetState);
+ SubscribeLocalEvent(OnMultipleToolHandleState);
+ }
+
+ private void OnMultipleToolHandleState(EntityUid uid, SharedMultipleToolComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not MultipleToolComponentState state)
+ return;
+
+ component.CurrentEntry = state.Selected;
+ SetMultipleTool(uid, component);
+ }
+
+ private void OnMultipleToolStartup(EntityUid uid, SharedMultipleToolComponent multiple, ComponentStartup args)
+ {
+ // Only set the multiple tool if we have a tool component.
+ if(EntityManager.TryGetComponent(uid, out ToolComponent? tool))
+ SetMultipleTool(uid, multiple, tool);
+ }
+
+ private void OnMultipleToolActivated(EntityUid uid, SharedMultipleToolComponent multiple, ActivateInWorldEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ args.Handled = CycleMultipleTool(uid, multiple, args.User);
+ }
+
+ private void OnMultipleToolGetState(EntityUid uid, SharedMultipleToolComponent multiple, ref ComponentGetState args)
+ {
+ args.State = new MultipleToolComponentState(multiple.CurrentEntry);
+ }
+
+ public bool CycleMultipleTool(EntityUid uid, SharedMultipleToolComponent? multiple = null, EntityUid? user = null)
+ {
+ if (!Resolve(uid, ref multiple))
+ return false;
+
+ if (multiple.Entries.Length == 0)
+ return false;
+
+ multiple.CurrentEntry = (uint) ((multiple.CurrentEntry + 1) % multiple.Entries.Length);
+ SetMultipleTool(uid, multiple, playSound: true, user: user);
+
+ return true;
+ }
+
+ public virtual void SetMultipleTool(EntityUid uid,
+ SharedMultipleToolComponent? multiple = null,
+ ToolComponent? tool = null,
+ bool playSound = false,
+ EntityUid? user = null)
+ {
+ if (!Resolve(uid, ref multiple, ref tool))
+ return;
+
+ Dirty(multiple);
+
+ if (multiple.Entries.Length <= multiple.CurrentEntry)
+ {
+ multiple.CurrentQualityName = Loc.GetString("multiple-tool-component-no-behavior");
+ return;
+ }
+
+ var current = multiple.Entries[multiple.CurrentEntry];
+ tool.UseSound = current.Sound;
+ tool.Qualities = current.Behavior;
+
+ if (playSound && current.ChangeSound != null)
+ _audioSystem.PlayPredicted(current.ChangeSound, uid, user);
+
+ if (_protoMan.TryIndex(current.Behavior.First(), out ToolQualityPrototype? quality))
+ multiple.CurrentQualityName = Loc.GetString(quality.Name);
+ }
+}
+
From f48bb549b92f197e1a5339e5e38b3a8f78a46b00 Mon Sep 17 00:00:00 2001
From: HoidC <99100948+HoidC@users.noreply.github.com>
Date: Tue, 16 Aug 2022 12:16:17 +0100
Subject: [PATCH 005/109] Rebalance some hardsuits to be better/make more sense
(RD, death squad) and renamed captain's armour (#10618)
---
.../Clothing/OuterClothing/hardsuits.yml | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
index 0d2526d732..e95f524980 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
@@ -32,8 +32,8 @@
- type: entity
parent: ClothingOuterHardsuitBase
id: ClothingOuterHardsuitCap
- name: captain's armor
- description: A suit of protective formal armor made for the station's captain.
+ name: captain's armored spacesuit
+ description: A formal armored spacesuit, made for the station's captain.
components:
- type: Sprite
sprite: Clothing/OuterClothing/Hardsuits/capspace.rsi
@@ -83,7 +83,7 @@
Heat: 0.2
Radiation: 0.2
- type: ExplosionResistance
- damageCoefficient: 0.7
+ damageCoefficient: 0.2
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitDeathsquad
@@ -139,7 +139,7 @@
Slash: 0.85
Piercing: 0.8
Heat: 0.4
- Radiation: 0.25
+ Radiation: 0.0
- type: ExplosionResistance
damageCoefficient: 0.2
- type: ToggleableClothing
@@ -237,21 +237,21 @@
- type: Clothing
sprite: Clothing/OuterClothing/Hardsuits/rd.rsi
- type: PressureProtection
- highPressureMultiplier: 0.05
+ highPressureMultiplier: 0.02
lowPressureMultiplier: 5500
- type: Armor
modifiers:
coefficients:
Blunt: 0.6
- Slash: 0.75
+ Slash: 0.8
Piercing: 0.95
- Heat: 0.65
- Radiation: 0.25
+ Heat: 0.3
+ Radiation: 0.1
- type: ClothingSpeedModifier
walkModifier: 0.75
sprintModifier: 0.75
- type: ExplosionResistance
- damageCoefficient: 0.3
+ damageCoefficient: 0.1
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitRd
- type: StaticPrice
@@ -345,7 +345,7 @@
parent: ClothingOuterHardsuitBase
id: ClothingOuterHardsuitSyndie
name: blood red hardsuit
- description: A dual-mode advanced hardsuit designed for work in special operations.
+ description: A heavily armored and agile advanced hardsuit designed for work in special operations.
components:
- type: Sprite
sprite: Clothing/OuterClothing/Hardsuits/syndicate.rsi
From ea43fb18c08a58351db69e8a5c029b85daf74d68 Mon Sep 17 00:00:00 2001
From: PJBot
Date: Tue, 16 Aug 2022 07:17:21 -0400
Subject: [PATCH 006/109] Automatic changelog update
---
Resources/Changelog/Changelog.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index a82e15ba29..8f07c9155e 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,10 +1,4 @@
Entries:
-- author: WilliamECrew
- changes:
- - {message: 'When dumping contents out of a bag, items will now be spread out randomly
- instead of all being dumped into the same exact spot.', type: Tweak}
- id: 1663
- time: '2022-06-06T00:34:44.0000000+00:00'
- author: Rane
changes:
- {message: Stethoscopes have been ported over from Nyanotrasen. Wear one on your
@@ -2934,3 +2928,9 @@ Entries:
\ for exciting, fun objects!", type: Add}
id: 2162
time: '2022-08-16T09:57:40.0000000+00:00'
+- author: Hoid
+ changes:
+ - {message: Experimental Research Hardsuit now protects more effectively against
+ things that can go wrong in the lab - including explosions., type: Tweak}
+ id: 2163
+ time: '2022-08-16T11:16:18.0000000+00:00'
From 69c0990075e159658e24957abc389e23b9c58c05 Mon Sep 17 00:00:00 2001
From: Martin Petkovski <63034378+martin69420@users.noreply.github.com>
Date: Tue, 16 Aug 2022 23:41:25 +1000
Subject: [PATCH 007/109] fixed missing magicarp and holocarp death sprite
(#10639)
---
.../Carps/holo.rsi/{dead.png => base_dead.png} | Bin
.../Textures/Mobs/Aliens/Carps/holo.rsi/meta.json | 2 +-
.../Carps/magic.rsi/{dead.png => base_dead.png} | Bin
.../Textures/Mobs/Aliens/Carps/magic.rsi/meta.json | 2 +-
4 files changed, 2 insertions(+), 2 deletions(-)
rename Resources/Textures/Mobs/Aliens/Carps/holo.rsi/{dead.png => base_dead.png} (100%)
rename Resources/Textures/Mobs/Aliens/Carps/magic.rsi/{dead.png => base_dead.png} (100%)
diff --git a/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/dead.png b/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/base_dead.png
similarity index 100%
rename from Resources/Textures/Mobs/Aliens/Carps/holo.rsi/dead.png
rename to Resources/Textures/Mobs/Aliens/Carps/holo.rsi/base_dead.png
diff --git a/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/meta.json b/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/meta.json
index da6dadc94b..3fbb43275a 100644
--- a/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/meta.json
+++ b/Resources/Textures/Mobs/Aliens/Carps/holo.rsi/meta.json
@@ -11,7 +11,7 @@
"name": "icon"
},
{
- "name": "dead",
+ "name": "base_dead",
"delays": [
[
0.1,
diff --git a/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/dead.png b/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/base_dead.png
similarity index 100%
rename from Resources/Textures/Mobs/Aliens/Carps/magic.rsi/dead.png
rename to Resources/Textures/Mobs/Aliens/Carps/magic.rsi/base_dead.png
diff --git a/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/meta.json b/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/meta.json
index cf70ff589b..9c8c90999c 100644
--- a/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/meta.json
+++ b/Resources/Textures/Mobs/Aliens/Carps/magic.rsi/meta.json
@@ -11,7 +11,7 @@
"name": "icon"
},
{
- "name": "dead"
+ "name": "base_dead"
},
{
"name": "base",
From ad67a8508a3f03590580b3a4792a11c5ea1f06a1 Mon Sep 17 00:00:00 2001
From: PJBot
Date: Tue, 16 Aug 2022 09:42:31 -0400
Subject: [PATCH 008/109] Automatic changelog update
---
Resources/Changelog/Changelog.yml | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index 8f07c9155e..3ffd9f771e 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,10 +1,4 @@
Entries:
-- author: Rane
- changes:
- - {message: Stethoscopes have been ported over from Nyanotrasen. Wear one on your
- neck and right click on someone else to listen to their chest., type: Add}
- id: 1664
- time: '2022-06-06T01:37:29.0000000+00:00'
- author: youarereadingthis
changes:
- {message: Removed the shuttle powering crate from the cargo ordering console.,
@@ -2934,3 +2928,8 @@ Entries:
things that can go wrong in the lab - including explosions., type: Tweak}
id: 2163
time: '2022-08-16T11:16:18.0000000+00:00'
+- author: martin69420
+ changes:
+ - {message: Fixed missing death sprite for holocarp and magicarp, type: Fix}
+ id: 2164
+ time: '2022-08-16T13:41:26.0000000+00:00'
From 93584f21dba8e74973d2f8c97720f55a65375812 Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Wed, 17 Aug 2022 12:47:58 +1200
Subject: [PATCH 009/109] Add entity prototype save test (#10274)
---
Content.IntegrationTests/Tests/EntityTest.cs | 1 +
.../Tests/PrototypeSaveTest.cs | 350 ++++++++++++++++++
Content.Server/Access/Systems/IdCardSystem.cs | 18 +-
.../Access/Systems/IdExaminableSystem.cs | 4 +-
.../Body/Components/BodyPartComponent.cs | 4 +-
Content.Server/Body/Systems/BodySystem.cs | 1 +
.../Tube/Components/DisposalTubeComponent.cs | 4 +-
.../Unit/EntitySystems/DisposalUnitSystem.cs | 3 +-
Content.Server/Mind/Commands/RenameCommand.cs | 2 +-
.../Nutrition/EntitySystems/DrinkSystem.cs | 8 +-
.../EntitySystems/TrashOnEmptySystem.cs | 2 -
Content.Server/PDA/Ringer/RingerSystem.cs | 4 +-
.../EntitySystems/EntityStorageSystem.cs | 2 +-
.../EntitySystems/SecretStashSystem.cs | 3 +-
.../Access/Components/AccessComponent.cs | 7 +-
.../Access/Components/IdCardComponent.cs | 4 +-
Content.Shared/Access/Systems/AccessSystem.cs | 4 +-
.../Components/SharedBodyPartComponent.cs | 2 +
.../Components/SharedDisposalUnitComponent.cs | 2 +
.../Systems/SharedGunSystem.Ballistic.cs | 11 +-
Resources/Prototypes/Body/Parts/animal.yml | 5 +
Resources/Prototypes/Body/Parts/human.yml | 5 +
Resources/Prototypes/Body/Parts/reptilian.yml | 5 +
Resources/Prototypes/Body/Parts/silicon.yml | 5 +
Resources/Prototypes/Body/Parts/skeleton.yml | 5 +
Resources/Prototypes/Body/Parts/slime.yml | 5 +
Resources/Prototypes/Body/Parts/vox.yml | 5 +
.../Catalog/Fills/Boxes/ammunition.yml | 26 +-
.../Entities/Clothing/Back/backpacks.yml | 4 +
.../Entities/Clothing/Back/duffel.yml | 4 +
.../Entities/Clothing/Back/satchel.yml | 4 +
.../Clothing/Belt/base_clothingbelt.yml | 4 +
.../Entities/Clothing/Head/helmets.yml | 4 +
.../OuterClothing/base_clothingouter.yml | 7 +
.../Entities/Clothing/OuterClothing/suits.yml | 3 +
.../Prototypes/Entities/Effects/puddle.yml | 2 +
.../Markers/Spawners/Conditional/timed.yml | 3 +
.../Prototypes/Entities/Mobs/NPCs/human.yml | 2 +-
.../Entities/Mobs/Player/guardian.yml | 1 +
.../Objects/Consumable/Drinks/drinks.yml | 2 +
.../Objects/Consumable/Drinks/drinks_cans.yml | 6 +
.../Objects/Consumable/Drinks/drinks_cups.yml | 2 +
.../Consumable/Drinks/drinks_special.yml | 2 +
.../Consumable/Drinks/trash_drinks.yml | 8 +
.../Consumable/Food/Containers/condiments.yml | 13 +-
.../Objects/Devices/holoprojectors.yml | 4 +
.../Entities/Objects/Devices/nuke.yml | 4 +
.../Entities/Objects/Devices/payload.yml | 5 +
.../Entities/Objects/Devices/pda.yml | 5 +
.../Instruments/instruments_structures.yml | 5 +-
.../Prototypes/Entities/Objects/Fun/toys.yml | 8 +
.../Prototypes/Entities/Objects/Misc/box.yml | 3 +
.../Entities/Objects/Misc/fluff_lights.yml | 2 +
.../Objects/Specific/Medical/morgue.yml | 5 +
.../Xenoarchaeology/artifact_equipment.yml | 5 +
.../Entities/Objects/Tools/flashlights.yml | 1 +
.../Entities/Objects/Tools/lantern.yml | 4 +
.../Guns/Ammunition/Boxes/antimaterial.yml | 4 +
.../Weapons/Guns/Ammunition/Boxes/toy.yml | 4 +
.../Guns/Ammunition/SpeedLoaders/magnum.yml | 4 +
.../Guns/Ammunition/SpeedLoaders/pistol.yml | 4 +
.../Ammunition/SpeedLoaders/rifle_light.yml | 4 +
.../Guns/Ammunition/SpeedLoaders/toy.yml | 4 +
.../Objects/Weapons/Guns/HMGs/hmgs.yml | 4 +
.../Weapons/Guns/Projectiles/projectiles.yml | 17 +-
.../Weapons/Guns/Revolvers/revolvers.yml | 16 +-
.../Weapons/Guns/Shotguns/shotguns.yml | 4 +
.../Objects/Weapons/Guns/Snipers/snipers.yml | 4 +
.../Objects/Weapons/Guns/flare_gun.yml | 4 +
.../Prototypes/Entities/Objects/base_item.yml | 5 +
.../Dispensers/base_structuredispensers.yml | 7 +
.../Entities/Structures/Dispensers/chem.yml | 3 -
.../Structures/Doors/Firelocks/firelock.yml | 7 +
.../Structures/Doors/Shutter/blast_door.yml | 6 +
.../Structures/Doors/Shutter/shutters.yml | 15 +
.../Structures/Furniture/potted_plants.yml | 3 +
.../Entities/Structures/Furniture/toilet.yml | 4 +
.../Computers/base_structurecomputers.yml | 4 +
.../Machines/Computers/computers.yml | 6 +
.../Machines/base_structuremachines.yml | 16 +
.../Structures/Machines/chem_master.yml | 15 +-
.../Structures/Machines/cloning_machine.yml | 5 +-
.../Entities/Structures/Machines/lathe.yml | 10 +-
.../Structures/Machines/medical_scanner.yml | 5 +-
.../Structures/Machines/reagent_grinder.yml | 5 +
.../Machines/surveillance_camera_routers.yml | 10 +-
.../Machines/wireless_surveillance_camera.yml | 5 +-
.../Structures/Piping/Atmospherics/miners.yml | 1 +
.../Structures/Piping/Atmospherics/pipes.yml | 1 +
.../Structures/Piping/Atmospherics/unary.yml | 14 +-
.../Structures/Piping/Disposal/pipes.yml | 5 +
.../Structures/Piping/Disposal/units.yml | 4 +
.../Power/Generation/Singularity/emitter.yml | 4 +-
.../Power/Generation/generators.yml | 18 +-
.../Entities/Structures/Power/cables.yml | 1 +
.../Entities/Structures/Power/chargers.yml | 4 +
.../Entities/Structures/Power/smes.yml | 9 +-
.../Entities/Structures/Power/substation.yml | 9 +-
.../Storage/Crates/base_structurecrates.yml | 10 +
.../Structures/Storage/Crates/crates.yml | 25 +-
.../Wallmounts/extinguisher_cabinet.yml | 4 +
.../Structures/Wallmounts/fireaxe_cabinet.yml | 4 +
.../Entities/Structures/Wallmounts/switch.yml | 16 +
.../Entities/Structures/hydro_tray.yml | 5 +-
104 files changed, 813 insertions(+), 144 deletions(-)
create mode 100644 Content.IntegrationTests/Tests/PrototypeSaveTest.cs
diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs
index d89a66488b..5966446dd4 100644
--- a/Content.IntegrationTests/Tests/EntityTest.cs
+++ b/Content.IntegrationTests/Tests/EntityTest.cs
@@ -21,6 +21,7 @@ namespace Content.IntegrationTests.Tests
public async Task SpawnTest()
{
//TODO: Run this test in a for loop, and figure out why garbage is ending up in the Entities list on cleanup.
+ //If this gets fixed, see also UninitializedSaveTest.
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, Dirty = true, Destructive = true});
var server = pairTracker.Pair.Server;
diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs
new file mode 100644
index 0000000000..617d55069d
--- /dev/null
+++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs
@@ -0,0 +1,350 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Content.Shared.Coordinates;
+using NUnit.Framework;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.Markdown.Value;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Content.IntegrationTests.Tests;
+
+///
+/// This test ensure that when an entity prototype is spawned into an un-initialized map, its component data is not
+/// modified during init. I.e., when the entity is saved to the map, its data is simply the default prototype data (ignoring transform component).
+///
+///
+/// If you are here becaus your test is failing, one easy way of figuring out how to fix the prototype is to just
+/// spawn it into a new empty map and seeing what the map yml looks like.
+///
+[TestFixture]
+public sealed class PrototypeSaveTest
+{
+ private readonly HashSet _ignoredPrototypes = new()
+ {
+ "Singularity", // physics collision uses "AllMask" (-1). The flag serializer currently fails to save this because this features un-named bits.
+ "constructionghost",
+
+ // TODO fix more prototypes
+ // The rest of these prototypes (probably) shouldn't be getting ignored.
+ // There should be an issue up tracking all of these prototypes, indicating that still need to get fixed.
+ "C4",
+ "WeaponProtoKineticAccelerator",
+ "WeaponStaffHealing",
+ "WeaponStaffPolymorphDoor",
+ "WeaponWandPolymorphCarp",
+ "WeaponWandPolymorphMonkey",
+ "WeaponWandFireball",
+ "WeaponWandDeath",
+ "WeaponWandPolymorphDoor",
+ "GlowstickBase",
+ "GlowstickRed",
+ "GlowstickPurple",
+ "GlowstickYellow",
+ "GlowstickBlue",
+ "Thruster",
+ "Gyroscope",
+ "RemoteSignaller",
+ "filingCabinet",
+ "filingCabinetTall",
+ "filingCabinetDrawer",
+ "WeaponLauncherChinaLake",
+ "WeaponLauncherRocket",
+ "WeaponLauncherMultipleRocket",
+ "Crematorium",
+ "JawsOfLife",
+ "SyndicateJawsOfLife",
+ "LightReplacer",
+ "ComputerCloningConsole",
+ "PowerDrill",
+ "Omnitool",
+ "GasPressurePump",
+ "GasVolumePump",
+ "GasDualPortVentPump",
+ "PortableScrubber",
+ "ParticleAcceleratorControlBox",
+ "GasFilter",
+ "GasFilterFlipped",
+ "GasMixer",
+ "GasMixerFlipped",
+ "HospitalCurtainsOpen",
+ "CargoPallet",
+ "DisposalHolder",
+ "ParticlesProjectile",
+ "AMEController",
+ "AMEControllerUnanchored",
+ "MopBucket",
+ "JanitorialTrolley",
+ "FloorDrain",
+ "OrganHumanLungs",
+ "SprayBottle",
+ "OrganRatLungs",
+ "SentientSlimeCore",
+ "OrganSlimeLungs",
+ "OrganVoxLungs",
+ "OrganAnimalLungs",
+ "Floodlight",
+ "EmergencyMedipen",
+ "AntiPoisonMedipen",
+ "SpaceMedipen",
+ "HolosignWetFloor",
+ "HeadSkeleton",
+ "PoweredlightEmpty",
+ "Poweredlight",
+ "PoweredlightLED",
+ "PoweredlightExterior",
+ "PoweredlightSodium",
+ "PoweredSmallLightEmpty",
+ "PoweredSmallLight",
+ "PoweredLightPostSmallEmpty",
+ "PoweredLightPostSmall",
+ "DeployableBarrier",
+ "CrateArtifactContainer",
+ "CloningPod",
+ "DrinkColaCan",
+ "FoodBowlBig",
+ "FoodBowlFancy",
+ "MachineFrame",
+ "WeaponImprovisedPneumaticCannon",
+ "LauncherCreamPie",
+ "GravityGenerator",
+ "GravityGeneratorMini",
+ "FoodCondimentPacket",
+ "FoodCondimentBottle",
+ "FoodCondimentBottleSmall",
+ "Autolathe",
+ "Protolathe",
+ "CircuitImprinter",
+ "SecurityTechFab",
+ "MedicalTechFab",
+ "UniformPrinter",
+ "OreProcessor",
+ "MedicalScanner",
+ "KitchenMicrowave",
+ "MagazinePistolSubMachineGunTopMounted",
+ "Recycler",
+ "EpinephrineChemistryBottle",
+ "RobustHarvestChemistryBottle",
+ "NocturineChemistryBottle",
+ "EphedrineChemistryBottle",
+ "OmnizineChemistryBottle",
+ "Beaker",
+ "LargeBeaker",
+ "CryostasisBeaker",
+ "BluespaceBeaker",
+ "Syringe",
+ "ClusterBang",
+ "ClusterBangFull",
+ "CargoTelepad",
+ "ClothingHeadHatHardhatBlue",
+ "ClothingHeadHatHardhatOrange",
+ "ClothingHeadHatHardhatRed",
+ "ClothingHeadHatHardhatWhite",
+ "ClothingHeadHatHardhatYellow",
+ "Vaccinator",
+ "AirlockExternalShuttleLocked",
+ "AirlockExternalGlassShuttleLocked",
+ "AirlockExternalGlassShuttleEmergencyLocked",
+ "ConveyorBelt",
+ "ClothingHeadHatChef",
+ "ClothingHeadHelmetFire",
+ "ClothingHeadHelmetAtmosFire",
+ "Bucket",
+ "CableTerminal",
+ "AirlockShuttle",
+ "AirlockGlassShuttle"
+ };
+
+ [Test]
+ public async Task UninitializedSaveTest()
+ {
+ // Apparently SpawnTest fails to clean up properly. Due to the similarities, I'll assume this also fails.
+ await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, Dirty = true, Destructive = true });
+ var server = pairTracker.Pair.Server;
+
+ var mapManager = server.ResolveDependency();
+ var entityMan = server.ResolveDependency();
+ var prototypeMan = server.ResolveDependency();
+ var tileDefinitionManager = server.ResolveDependency();
+ var seriMan = server.ResolveDependency();
+ var compFact = server.ResolveDependency();
+
+ var prototypes = new List();
+ IMapGrid grid = default!;
+ EntityUid uid;
+ MapId mapId = default;
+
+ //Build up test environment
+ await server.WaitPost(() =>
+ {
+ // Create a one tile grid to stave off the grid 0 monsters
+ mapId = mapManager.CreateMap();
+
+ mapManager.AddUninitializedMap(mapId);
+
+ grid = mapManager.CreateGrid(mapId);
+
+ var tileDefinition = tileDefinitionManager["UnderPlating"];
+ var tile = new Tile(tileDefinition.TileId);
+ var coordinates = grid.ToCoordinates();
+
+ grid.SetTile(coordinates, tile);
+ });
+
+ await server.WaitRunTicks(5);
+
+ //Generate list of non-abstract prototypes to test
+ foreach (var prototype in prototypeMan.EnumeratePrototypes())
+ {
+ if (prototype.Abstract)
+ continue;
+
+ // Currently mobs and such can't be serialized, but they aren't flagged as serializable anyways.
+ if (!prototype.MapSavable)
+ continue;
+
+ if (_ignoredPrototypes.Contains(prototype.ID))
+ continue;
+
+ if (prototype.SetSuffix == "DEBUG")
+ continue;
+
+ prototypes.Add(prototype);
+ }
+
+ var context = new TestEntityUidContext();
+
+ await server.WaitAssertion(() =>
+ {
+ Assert.That(!mapManager.IsMapInitialized(mapId));
+ var testLocation = grid.ToCoordinates();
+
+ Assert.Multiple(() =>
+ {
+ //Iterate list of prototypes to spawn
+ foreach (var prototype in prototypes)
+ {
+ uid = entityMan.SpawnEntity(prototype.ID, testLocation);
+ server.RunTicks(1);
+
+ // get default prototype data
+ Dictionary protoData = new();
+ try
+ {
+ foreach (var (compType, comp) in prototype.Components)
+ {
+ protoData.Add(compType, seriMan.WriteValueAs(comp.Component.GetType(), comp.Component, context: context));
+ }
+ }
+ catch (Exception e)
+ {
+ Assert.Fail($"Failed to convert prototype {prototype.ID} into yaml. Exception: {e.Message}");
+ continue;
+ }
+
+ var comps = new HashSet(entityMan.GetComponents(uid));
+ var compNames = new HashSet(comps.Count);
+ foreach (var component in comps)
+ {
+ var compType = component.GetType();
+ var compName = compFact.GetComponentName(compType);
+ compNames.Add(compName);
+
+ if (compType == typeof(MetaDataComponent) || compType == typeof(TransformComponent))
+ continue;
+
+ MappingDataNode compMapping;
+ try
+ {
+ compMapping = seriMan.WriteValueAs(compType, component, context: context);
+ }
+ catch (Exception e)
+ {
+ Assert.Fail($"Failed to serialize {compName} component of entity prototype {prototype.ID}. Exception: {e.Message}");
+ continue;
+ }
+
+ if (protoData.TryGetValue(compName, out var protoMapping))
+ {
+ var diff = compMapping.Except(protoMapping);
+
+ if (diff != null && diff.Children.Count != 0)
+ {
+ var modComps = string.Join(",", diff.Keys.Select(x => x.ToString()));
+ Assert.Fail($"Prototype {prototype.ID} modifies component on spawn: {compName}. Modified fields: {modComps}");
+ }
+ }
+ else
+ {
+ Assert.Fail($"Prototype {prototype.ID} gains a component on spawn: {compName}");
+ }
+ }
+
+ // An entity may also remove components on init -> check no components are missing.
+ foreach (var (compType, comp) in prototype.Components)
+ {
+ Assert.That(compNames.Contains(compType), $"Prototype {prototype.ID} removes component {compType} on spawn.");
+ }
+
+ if (!entityMan.Deleted(uid))
+ entityMan.DeleteEntity(uid);
+ }
+ });
+ });
+ await pairTracker.CleanReturnAsync();
+ }
+
+ private sealed class TestEntityUidContext : ISerializationContext,
+ ITypeSerializer,
+ ITypeReaderWriter
+ {
+ public Dictionary<(Type, Type), object> TypeReaders { get; }
+ public Dictionary TypeWriters { get; }
+ public Dictionary TypeCopiers => TypeWriters;
+ public Dictionary<(Type, Type), object> TypeValidators => TypeReaders;
+
+ public TestEntityUidContext()
+ {
+ TypeReaders = new() { { (typeof(EntityUid), typeof(ValueDataNode)), this } };
+ TypeWriters = new() { { typeof(EntityUid), this } };
+ }
+
+ ValidationNode ITypeValidator.Validate(ISerializationManager serializationManager,
+ ValueDataNode node, IDependencyCollection dependencies, ISerializationContext? context)
+ {
+ return new ValidatedValueNode(node);
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, EntityUid value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ // EntityUids should be nullable and have no initial value.
+ throw new InvalidOperationException("Serializing prototypes should not attempt to write entity Uids");
+ }
+
+ EntityUid ITypeReader.Read(ISerializationManager serializationManager,
+ ValueDataNode node,
+ IDependencyCollection dependencies,
+ bool skipHook,
+ ISerializationContext? context, EntityUid _)
+ {
+ return EntityUid.Invalid;
+ }
+
+ public EntityUid Copy(ISerializationManager serializationManager, EntityUid source, EntityUid target,
+ bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return new((int) source);
+ }
+ }
+}
diff --git a/Content.Server/Access/Systems/IdCardSystem.cs b/Content.Server/Access/Systems/IdCardSystem.cs
index 53b3aef0af..d823376b2b 100644
--- a/Content.Server/Access/Systems/IdCardSystem.cs
+++ b/Content.Server/Access/Systems/IdCardSystem.cs
@@ -1,7 +1,3 @@
-using Content.Shared.Hands.Components;
-using Content.Shared.Inventory;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Kitchen.Components;
using Content.Server.Popups;
@@ -9,11 +5,11 @@ using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Database;
-using Content.Shared.PDA;
using Content.Shared.Popups;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using System.Linq;
namespace Content.Server.Access.Systems
{
@@ -27,13 +23,15 @@ namespace Content.Server.Access.Systems
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnMicrowaved);
}
- private void OnInit(EntityUid uid, IdCardComponent id, ComponentInit args)
+ private void OnMapInit(EntityUid uid, IdCardComponent id, MapInitEvent args)
{
- id.OriginalOwnerName ??= EntityManager.GetComponent(id.Owner).EntityName;
+ // On one hand, these prototypes should default to having the correct name. On the other hand, id cards are
+ // rarely ever spawned in on their own without an owner, so this is fine.
+ id.OriginalEntityName ??= EntityManager.GetComponent(id.Owner).EntityName;
UpdateEntityName(uid, id);
}
@@ -139,7 +137,7 @@ namespace Content.Server.Access.Systems
if (string.IsNullOrWhiteSpace(id.FullName) && string.IsNullOrWhiteSpace(id.JobTitle))
{
- EntityManager.GetComponent(id.Owner).EntityName = id.OriginalOwnerName;
+ EntityManager.GetComponent(id.Owner).EntityName = id.OriginalEntityName;
return;
}
@@ -147,7 +145,7 @@ namespace Content.Server.Access.Systems
var val = string.IsNullOrWhiteSpace(id.FullName)
? Loc.GetString("access-id-card-component-owner-name-job-title-text",
- ("originalOwnerName", id.OriginalOwnerName),
+ ("originalOwnerName", id.OriginalEntityName),
("jobSuffix", jobSuffix))
: Loc.GetString("access-id-card-component-owner-full-name-job-title-text",
("fullName", id.FullName),
diff --git a/Content.Server/Access/Systems/IdExaminableSystem.cs b/Content.Server/Access/Systems/IdExaminableSystem.cs
index d1edc3f199..2966624368 100644
--- a/Content.Server/Access/Systems/IdExaminableSystem.cs
+++ b/Content.Server/Access/Systems/IdExaminableSystem.cs
@@ -1,4 +1,4 @@
-using Content.Server.Access.Components;
+using Content.Server.Access.Components;
using Content.Shared.Access.Components;
using Content.Shared.Examine;
using Content.Shared.Inventory;
@@ -66,7 +66,7 @@ public sealed class IdExaminableSystem : EntitySystem
var val = string.IsNullOrWhiteSpace(id.FullName)
? Loc.GetString("access-id-card-component-owner-name-job-title-text",
- ("originalOwnerName", id.OriginalOwnerName),
+ ("originalOwnerName", id.OriginalEntityName),
("jobSuffix", jobSuffix))
: Loc.GetString("access-id-card-component-owner-full-name-job-title-text",
("fullName", id.FullName),
diff --git a/Content.Server/Body/Components/BodyPartComponent.cs b/Content.Server/Body/Components/BodyPartComponent.cs
index 4f465dda05..f9ea0334d3 100644
--- a/Content.Server/Body/Components/BodyPartComponent.cs
+++ b/Content.Server/Body/Components/BodyPartComponent.cs
@@ -33,11 +33,11 @@ namespace Content.Server.Body.Components
mechanism.Owner.RandomOffset(0.25f);
}
- protected override void Initialize()
+ public void MapInitialize()
{
base.Initialize();
- _mechanismContainer = Owner.EnsureContainer($"{Name}-{nameof(BodyPartComponent)}");
+ _mechanismContainer = Owner.EnsureContainer(ContainerId);
// This is ran in Startup as entities spawned in Initialize
// are not synced to the client since they are assumed to be
diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs
index 161b2d27e2..fca8f0d166 100644
--- a/Content.Server/Body/Systems/BodySystem.cs
+++ b/Content.Server/Body/Systems/BodySystem.cs
@@ -21,6 +21,7 @@ namespace Content.Server.Body.Systems
SubscribeLocalEvent(OnRelayMoveInput);
SubscribeLocalEvent(OnApplyMetabolicMultiplier);
SubscribeLocalEvent(OnBeingMicrowaved);
+ SubscribeLocalEvent((_, c, _) => c.MapInitialize());
}
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
index ff60c107c7..3692937930 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
@@ -12,6 +12,8 @@ namespace Content.Server.Disposal.Tube.Components
{
public abstract class DisposalTubeComponent : Component, IDisposalTubeComponent
{
+ public const string ContainerId = "disposal-tube";
+
[Dependency] private readonly IEntityManager _entMan = default!;
public static readonly TimeSpan ClangDelay = TimeSpan.FromSeconds(0.5);
@@ -135,7 +137,7 @@ namespace Content.Server.Disposal.Tube.Components
{
base.Initialize();
- Contents = ContainerHelpers.EnsureContainer(Owner, Name);
+ Contents = ContainerHelpers.EnsureContainer(Owner, ContainerId);
Owner.EnsureComponent();
}
diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
index 5835c0b4e4..25b78ac030 100644
--- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
+++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
@@ -46,6 +46,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly DumpableSystem _dumpableSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
private readonly List _activeDisposals = new();
@@ -293,7 +294,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
private void HandleDisposalInit(EntityUid uid, DisposalUnitComponent component, ComponentInit args)
{
- component.Container = component.Owner.EnsureContainer(component.Name);
+ component.Container = _containerSystem.EnsureContainer(uid, SharedDisposalUnitComponent.ContainerId);
UpdateInterface(component, component.Powered);
diff --git a/Content.Server/Mind/Commands/RenameCommand.cs b/Content.Server/Mind/Commands/RenameCommand.cs
index 40c989d226..23b20b95f4 100644
--- a/Content.Server/Mind/Commands/RenameCommand.cs
+++ b/Content.Server/Mind/Commands/RenameCommand.cs
@@ -62,7 +62,7 @@ public sealed class RenameCommand : IConsoleCommand
{
foreach (var idCardComponent in entMan.EntityQuery())
{
- if (idCardComponent.OriginalOwnerName != oldName)
+ if (idCardComponent.OriginalEntityName != oldName)
continue;
idCardSystem.TryChangeFullName(idCardComponent.Owner, name, idCardComponent);
}
diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
index cd8874c1c3..c5e7e655a8 100644
--- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
@@ -182,9 +182,11 @@ namespace Content.Server.Nutrition.EntitySystems
UpdateAppearance(component);
- // Synchronize solution in drink
- EnsureComp(uid).Solution = component.SolutionName;
- EnsureComp(uid).Solution = component.SolutionName;
+ if (TryComp(uid, out RefillableSolutionComponent? refillComp))
+ refillComp.Solution = component.SolutionName;
+
+ if (TryComp(uid, out DrainableSolutionComponent? drainComp))
+ drainComp.Solution = component.SolutionName;
}
private void OnSolutionChange(EntityUid uid, DrinkComponent component, SolutionChangedEvent args)
diff --git a/Content.Server/Nutrition/EntitySystems/TrashOnEmptySystem.cs b/Content.Server/Nutrition/EntitySystems/TrashOnEmptySystem.cs
index b58a2185dd..c0d57244a1 100644
--- a/Content.Server/Nutrition/EntitySystems/TrashOnEmptySystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/TrashOnEmptySystem.cs
@@ -30,8 +30,6 @@ namespace Content.Server.Nutrition.EntitySystems
public void CheckSolutions(TrashOnEmptyComponent component)
{
- EntityManager.EnsureComponent(component.Owner);
-
if (!EntityManager.HasComponent((component).Owner))
return;
diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs
index 95d138a9d2..dd4394149b 100644
--- a/Content.Server/PDA/Ringer/RingerSystem.cs
+++ b/Content.Server/PDA/Ringer/RingerSystem.cs
@@ -18,7 +18,7 @@ namespace Content.Server.PDA.Ringer
base.Initialize();
// General Event Subscriptions
- SubscribeLocalEvent(RandomizeRingtone);
+ SubscribeLocalEvent(RandomizeRingtone);
// RingerBoundUserInterface Subscriptions
SubscribeLocalEvent(OnSetRingtone);
SubscribeLocalEvent(RingerPlayRingtone);
@@ -46,7 +46,7 @@ namespace Content.Server.PDA.Ringer
UpdateRingerRingtone(ringer, args.Ringtone);
}
- public void RandomizeRingtone(EntityUid uid, RingerComponent ringer, ComponentInit args)
+ public void RandomizeRingtone(EntityUid uid, RingerComponent ringer, MapInitEvent args)
{
// Default to using C pentatonic so it at least sounds not terrible.
var notes = new[]
diff --git a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
index 75ca46aa49..221d359f2c 100644
--- a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
+++ b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
@@ -50,7 +50,7 @@ public sealed class EntityStorageSystem : EntitySystem
component.Contents.OccludesLight = component.OccludesLight;
if (TryComp(uid, out var construction))
- _construction.AddContainer(uid, nameof(EntityStorageComponent), construction);
+ _construction.AddContainer(uid, ContainerName, construction);
if (TryComp(uid, out var placeable))
_placeableSurface.SetPlaceable(uid, component.Open, placeable);
diff --git a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs b/Content.Server/Storage/EntitySystems/SecretStashSystem.cs
index 484cda4d5a..cc82436683 100644
--- a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs
+++ b/Content.Server/Storage/EntitySystems/SecretStashSystem.cs
@@ -13,6 +13,7 @@ namespace Content.Server.Storage.EntitySystems
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
@@ -31,7 +32,7 @@ namespace Content.Server.Storage.EntitySystems
component.SecretPartName = entityName;
}
- component.ItemContainer = ContainerHelpers.EnsureContainer(uid, "stash", out _);
+ component.ItemContainer = _containerSystem.EnsureContainer(uid, "stash", out _);
}
private void OnDestroyed(EntityUid uid, SecretStashComponent component, DestructionEventArgs args)
diff --git a/Content.Shared/Access/Components/AccessComponent.cs b/Content.Shared/Access/Components/AccessComponent.cs
index b063f9b40c..8cd5146ed8 100644
--- a/Content.Shared/Access/Components/AccessComponent.cs
+++ b/Content.Shared/Access/Components/AccessComponent.cs
@@ -14,7 +14,10 @@ namespace Content.Shared.Access.Components
[Access(typeof(AccessSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public HashSet Tags = new();
- [DataField("groups", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
- public HashSet Groups = new();
+ ///
+ /// Access Groups. These are added to the tags during map init. After map init this will have no effect.
+ ///
+ [DataField("groups", readOnly: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public readonly HashSet Groups = new();
}
}
diff --git a/Content.Shared/Access/Components/IdCardComponent.cs b/Content.Shared/Access/Components/IdCardComponent.cs
index 43395a7a5c..f63504e157 100644
--- a/Content.Shared/Access/Components/IdCardComponent.cs
+++ b/Content.Shared/Access/Components/IdCardComponent.cs
@@ -9,8 +9,8 @@ namespace Content.Shared.Access.Components
[Access(typeof(SharedIdCardSystem), typeof(SharedPDASystem), typeof(SharedAgentIdCardSystem))]
public sealed class IdCardComponent : Component
{
- [DataField("originalOwnerName")]
- public string OriginalOwnerName = default!;
+ [DataField("originalEntityName")]
+ public string OriginalEntityName = string.Empty;
[DataField("fullName")]
[Access(typeof(SharedIdCardSystem), typeof(SharedPDASystem), typeof(SharedAgentIdCardSystem),
diff --git a/Content.Shared/Access/Systems/AccessSystem.cs b/Content.Shared/Access/Systems/AccessSystem.cs
index c1b2f02317..56e2fcc031 100644
--- a/Content.Shared/Access/Systems/AccessSystem.cs
+++ b/Content.Shared/Access/Systems/AccessSystem.cs
@@ -12,10 +12,10 @@ namespace Content.Shared.Access.Systems
{
base.Initialize();
- SubscribeLocalEvent(OnAccessInit);
+ SubscribeLocalEvent(OnAccessInit);
}
- private void OnAccessInit(EntityUid uid, AccessComponent component, ComponentInit args)
+ private void OnAccessInit(EntityUid uid, AccessComponent component, MapInitEvent args)
{
// Add all tags in groups to the list of tags.
foreach (var group in component.Groups)
diff --git a/Content.Shared/Body/Components/SharedBodyPartComponent.cs b/Content.Shared/Body/Components/SharedBodyPartComponent.cs
index 6d8e360858..5f2b790779 100644
--- a/Content.Shared/Body/Components/SharedBodyPartComponent.cs
+++ b/Content.Shared/Body/Components/SharedBodyPartComponent.cs
@@ -11,6 +11,8 @@ namespace Content.Shared.Body.Components
[NetworkedComponent()]
public abstract class SharedBodyPartComponent : Component
{
+ public const string ContainerId = "bodypart";
+
[Dependency] private readonly IEntityManager _entMan = default!;
private SharedBodyComponent? _body;
diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
index b93c15dcab..493c705d2b 100644
--- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
+++ b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
@@ -6,6 +6,8 @@ namespace Content.Shared.Disposal.Components
[NetworkedComponent]
public abstract class SharedDisposalUnitComponent : Component
{
+ public const string ContainerId = "disposal-unit";
+
// TODO: Could maybe turn the contact off instead far more cheaply as farseer (though not box2d) had support for it?
// Need to suss it out.
///
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
index 56d89a048e..5bc7cf9d5d 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
@@ -16,6 +16,7 @@ public abstract partial class SharedGunSystem
protected virtual void InitializeBallistic()
{
SubscribeLocalEvent(OnBallisticInit);
+ SubscribeLocalEvent(OnBallisticMapInit);
SubscribeLocalEvent(OnBallisticTakeAmmo);
SubscribeLocalEvent(OnBallisticAmmoCount);
SubscribeLocalEvent(OnBallisticGetState);
@@ -122,16 +123,12 @@ public abstract partial class SharedGunSystem
private void OnBallisticInit(EntityUid uid, BallisticAmmoProviderComponent component, ComponentInit args)
{
component.Container = Containers.EnsureContainer(uid, "ballistic-ammo");
- component.UnspawnedCount = component.Capacity;
+ }
+ private void OnBallisticMapInit(EntityUid uid, BallisticAmmoProviderComponent component, MapInitEvent args)
+ {
if (component.FillProto != null)
- {
component.UnspawnedCount -= Math.Min(component.UnspawnedCount, component.Container.ContainedEntities.Count);
- }
- else
- {
- component.UnspawnedCount = 0;
- }
}
protected int GetBallisticShots(BallisticAmmoProviderComponent component)
diff --git a/Resources/Prototypes/Body/Parts/animal.yml b/Resources/Prototypes/Body/Parts/animal.yml
index 422736cda8..c2aa321d64 100644
--- a/Resources/Prototypes/Body/Parts/animal.yml
+++ b/Resources/Prototypes/Body/Parts/animal.yml
@@ -10,6 +10,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
# For primates mainly
- type: entity
diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml
index 689a104e8a..874b306a4e 100644
--- a/Resources/Prototypes/Body/Parts/human.yml
+++ b/Resources/Prototypes/Body/Parts/human.yml
@@ -8,6 +8,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: TorsoHuman
diff --git a/Resources/Prototypes/Body/Parts/reptilian.yml b/Resources/Prototypes/Body/Parts/reptilian.yml
index 1f2183a48d..709bd71ac8 100644
--- a/Resources/Prototypes/Body/Parts/reptilian.yml
+++ b/Resources/Prototypes/Body/Parts/reptilian.yml
@@ -8,6 +8,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: TorsoReptilian
diff --git a/Resources/Prototypes/Body/Parts/silicon.yml b/Resources/Prototypes/Body/Parts/silicon.yml
index 581634f985..05eb122dbd 100644
--- a/Resources/Prototypes/Body/Parts/silicon.yml
+++ b/Resources/Prototypes/Body/Parts/silicon.yml
@@ -6,6 +6,11 @@
components:
- type: Damageable
damageContainer: Inorganic
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: LeftArmBorg
diff --git a/Resources/Prototypes/Body/Parts/skeleton.yml b/Resources/Prototypes/Body/Parts/skeleton.yml
index b4e9d2e8a7..9494f6b5c6 100644
--- a/Resources/Prototypes/Body/Parts/skeleton.yml
+++ b/Resources/Prototypes/Body/Parts/skeleton.yml
@@ -7,6 +7,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: TorsoSkeleton
diff --git a/Resources/Prototypes/Body/Parts/slime.yml b/Resources/Prototypes/Body/Parts/slime.yml
index 53c8196165..9c3c11747a 100644
--- a/Resources/Prototypes/Body/Parts/slime.yml
+++ b/Resources/Prototypes/Body/Parts/slime.yml
@@ -7,6 +7,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: TorsoSlime
diff --git a/Resources/Prototypes/Body/Parts/vox.yml b/Resources/Prototypes/Body/Parts/vox.yml
index e54dbf715f..5bbd4e15fb 100644
--- a/Resources/Prototypes/Body/Parts/vox.yml
+++ b/Resources/Prototypes/Body/Parts/vox.yml
@@ -8,6 +8,11 @@
components:
- type: Damageable
damageContainer: Biological
+ - type: BodyPart
+ - type: ContainerContainer
+ containers:
+ bodypart: !type:Container
+ ents: []
- type: entity
id: TorsoVox
diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml b/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
index c1fc8ee73b..9e9cc16cc0 100644
--- a/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
+++ b/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
@@ -367,10 +367,22 @@
- id: MagazineShotgunIncendiary
amount: 6
+# base BallisticAmmoProvider boxes
+- type: entity
+ parent: BoxCardboard
+ id: BoxAmmoProvider
+ abstract: true
+ components:
+ - type: BallisticAmmoProvider
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ storagebase: !type:Container
+
# Shotgun Shells
- type: entity
name: box of shotgun beanbag cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxBeanbag
description: A box full of beanbag shots, designed for riot shotguns.
components:
@@ -384,7 +396,7 @@
- type: entity
name: box of shotgun lethal cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxLethalshot
description: A box full of lethal pellet shots, designed for riot shotguns.
components:
@@ -398,7 +410,7 @@
- type: entity
name: box of shotgun slug cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxShotgunSlug
description: A box full of shotgun slugs, designed for riot shotguns.
components:
@@ -412,7 +424,7 @@
- type: entity
name: box of shotgun flare cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxShotgunFlare
description: A box full of shotgun flare cartridges, designed for riot shotguns.
components:
@@ -426,7 +438,7 @@
- type: entity
name: box of shotgun incendiary cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxShotgunIncendiary
description: A box full of shotgun incendiary cartridges, designed for riot shotguns.
components:
@@ -440,7 +452,7 @@
- type: entity
name: box of shotgun practice cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxShotgunPractice
description: A box full of shotgun practice cartridges, designed for riot shotguns.
components:
@@ -454,7 +466,7 @@
- type: entity
name: box of tranquilizer cartridges
- parent: BoxCardboard
+ parent: BoxAmmoProvider
id: BoxShellTranquilizer
description: A box full of tranquilizer cartridges, designed for riot shotguns.
components:
diff --git a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
index 0ca1cb8d9a..335a994a3a 100644
--- a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
+++ b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
@@ -15,6 +15,10 @@
- back
- type: Storage
capacity: 100
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
- type: UserInterface
interfaces:
- key: enum.StorageUiKey.Key
diff --git a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml
index 04c44d319f..17d09be0f7 100644
--- a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml
+++ b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml
@@ -15,6 +15,10 @@
- back
- type: Storage
capacity: 120
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
- type: ClothingSpeedModifier
walkModifier: 1
sprintModifier: 0.9
diff --git a/Resources/Prototypes/Entities/Clothing/Back/satchel.yml b/Resources/Prototypes/Entities/Clothing/Back/satchel.yml
index c3c665ca31..5abef34c03 100644
--- a/Resources/Prototypes/Entities/Clothing/Back/satchel.yml
+++ b/Resources/Prototypes/Entities/Clothing/Back/satchel.yml
@@ -15,6 +15,10 @@
- back
- type: Storage
capacity: 100
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
- type: UserInterface
interfaces:
- key: enum.StorageUiKey.Key
diff --git a/Resources/Prototypes/Entities/Clothing/Belt/base_clothingbelt.yml b/Resources/Prototypes/Entities/Clothing/Belt/base_clothingbelt.yml
index cefc20503d..d23807a1fa 100644
--- a/Resources/Prototypes/Entities/Clothing/Belt/base_clothingbelt.yml
+++ b/Resources/Prototypes/Entities/Clothing/Belt/base_clothingbelt.yml
@@ -20,6 +20,10 @@
capacity: 40
equipSound:
path: /Audio/Items/belt_equip.ogg
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
- type: UserInterface
interfaces:
- key: enum.StorageUiKey.Key
diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml
index 4371b826c2..e35486c745 100644
--- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml
+++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml
@@ -221,6 +221,10 @@
Heat: 0.65
Radiation: 1
- type: IdentityBlocker
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ cell_slot: !type:ContainerSlot {}
- type: entity
parent: ClothingHeadLightBase
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
index 75d860235d..a577a9168a 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
@@ -30,6 +30,10 @@
components:
- type: Storage
capacity: 10
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
- type: UserInterface
interfaces:
- key: enum.StorageUiKey.Key
@@ -62,6 +66,9 @@
- type: DiseaseProtection
protection: 0.05
- type: ToggleableClothing
+ - type: ContainerContainer
+ containers:
+ toggleable-clothing: !type:ContainerSlot {}
- type: entity
abstract: true
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml
index 23c6a60be1..e80338f302 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml
@@ -35,6 +35,9 @@
coefficient: 0.5
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetEVA
+ - type: ContainerContainer
+ containers:
+ toggleable-clothing: !type:ContainerSlot {}
- type: entity
parent: ClothingOuterBaseLarge
diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml
index a24b5534b5..d790eeea42 100644
--- a/Resources/Prototypes/Entities/Effects/puddle.yml
+++ b/Resources/Prototypes/Entities/Effects/puddle.yml
@@ -8,6 +8,8 @@
- type: Sprite
drawdepth: FloorObjects
- type: SolutionContainerManager
+ solutions:
+ puddle: {}
- type: Puddle
spillSound:
path: /Audio/Effects/Fluids/splat.ogg
diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Conditional/timed.yml b/Resources/Prototypes/Entities/Markers/Spawners/Conditional/timed.yml
index 3c1de0b395..28cec0607f 100644
--- a/Resources/Prototypes/Entities/Markers/Spawners/Conditional/timed.yml
+++ b/Resources/Prototypes/Entities/Markers/Spawners/Conditional/timed.yml
@@ -8,6 +8,7 @@
- state: blue
- texture: Mobs/Species/Human/parts.rsi/full.png
- state: ai
+ - type: Timer
- type: TimedSpawner
prototypes:
- MobSpirate
@@ -27,6 +28,7 @@
- state: blue
- texture: Mobs/Aliens/Xenos/burrower.rsi/crit.png
- state: ai
+ - type: Timer
- type: TimedSpawner
prototypes:
- MobXeno
@@ -45,6 +47,7 @@
- state: blue
- texture: Mobs/Animals/mouse.rsi/icon-2.png
- state: timed
+ - type: Timer
- type: TimedSpawner
prototypes:
- MobMouse
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml
index db902e3921..9bfbf528e2 100644
--- a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml
@@ -51,7 +51,7 @@
- type: entity
parent: BaseMobHuman
suffix: Dead
- save: true
+ save: false # mobs are currently not saveable.
id: SalvageHumanCorpse
name: unidentified human
description: We barely knew ye.
diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml
index db46da8d3e..5ef64f67cb 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml
@@ -4,6 +4,7 @@
name: GuardianBase
id: MobGuardianBase
description: guardian
+ save: false
components:
- type: GhostTakeoverAvailable
makeSentient: true
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml
index 62f709fa79..780edc5391 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml
@@ -22,6 +22,8 @@
solution: drink
- type: RefillableSolution
solution: drink
+ - type: DrainableSolution
+ solution: drink
- type: UserInterface
interfaces:
- key: enum.TransferAmountUiKey.Key
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml
index e4d1663d87..0665c6dc56 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml
@@ -27,6 +27,12 @@
- state: icon
map: ["enum.DrinkCanVisualLayers.Icon"]
netsync: false
+ - type: FitsInDispenser
+ solution: drink
+ - type: DrawableSolution
+ solution: drink
+ - type: RefillableSolution
+ solution: drink
- type: DrainableSolution
solution: drink
- type: Appearance
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml
index 187e83f14c..6ddcfee053 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml
@@ -15,6 +15,8 @@
solution: drink
- type: RefillableSolution
solution: drink
+ - type: DrainableSolution
+ solution: drink
- type: SolutionTransfer
canChangeTransferAmount: true
maxTransferAmount: 10
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml
index 56744e95c7..a7c6e1098f 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml
@@ -16,6 +16,8 @@
solution: drink
- type: RefillableSolution
solution: drink
+ - type: DrainableSolution
+ solution: drink
- type: SolutionTransfer
canChangeTransferAmount: true
- type: Spillable
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml
index 4992feed90..010bb73504 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml
@@ -19,6 +19,14 @@
isOpen: true
- type: Spillable
solution: drink
+ - type: FitsInDispenser
+ solution: drink
+ - type: DrawableSolution
+ solution: drink
+ - type: RefillableSolution
+ solution: drink
+ - type: DrainableSolution
+ solution: drink
- type: UserInterface
interfaces:
- key: enum.TransferAmountUiKey.Key
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml
index 100c6a0a2c..2eca61dda2 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/condiments.yml
@@ -11,6 +11,7 @@
components:
- type: Drink
solution: food
+ refillable: false
openSounds:
collection: packetOpenSounds
- type: DrawableSolution
@@ -19,7 +20,9 @@
solution: food
- type: DrainableSolution
solution: food
- # Note NOT refillable
+ # Note NOT refillable.
+ # It be a shame if it turned out ALL drinks were ALWAYS refillable.... ffs.
+ # Well its fixed now, but I want to share my pain.
- type: SolutionContainerManager
solutions:
food:
@@ -331,6 +334,10 @@
solution: food
openSounds:
collection: pop
+ - type: RefillableSolution
+ solution: food
+ - type: DrainableSolution
+ solution: food
- type: SolutionContainerManager
solutions:
food:
@@ -478,6 +485,10 @@
solution: food
openSounds:
collection: pop
+ - type: RefillableSolution
+ solution: food
+ - type: DrainableSolution
+ solution: food
- type: SolutionContainerManager
solutions:
food:
diff --git a/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml b/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml
index 1bfa5164dc..e99c0d00d7 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml
@@ -8,6 +8,10 @@
- type: ItemCooldown
- type: UseDelay
delay: 1.0
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ cell_slot: !type:ContainerSlot {}
- type: PowerCellSlot
cellSlot:
startingItem: PowerCellMedium
diff --git a/Resources/Prototypes/Entities/Objects/Devices/nuke.yml b/Resources/Prototypes/Entities/Objects/Devices/nuke.yml
index c1a04d0599..4be867b7be 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/nuke.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/nuke.yml
@@ -48,6 +48,10 @@
- type: StaticPrice
price: 50000 # YOU STOLE A NUCLEAR FISSION EXPLOSIVE?!
- type: CargoSellBlacklist
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ Nuke: !type:ContainerSlot
- type: entity
parent: NuclearBomb
diff --git a/Resources/Prototypes/Entities/Objects/Devices/payload.yml b/Resources/Prototypes/Entities/Objects/Devices/payload.yml
index 86575c8563..fce48952e0 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/payload.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/payload.yml
@@ -76,6 +76,11 @@
enum.ChemicalPayloadFilledSlots.Left: payload-chemical-left
enum.ChemicalPayloadFilledSlots.Right: payload-chemical-right
enum.ChemicalPayloadFilledSlots.Both: payload-chemical-armed
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ BeakerSlotA: !type:ContainerSlot
+ BeakerSlotB: !type:ContainerSlot
- type: entity
name: flash payload
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index 24166704f0..375f57b865 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -17,6 +17,11 @@
state: pda
- type: Item
size: 10
+ - type: ContainerContainer
+ containers:
+ PDA-id: !type:ContainerSlot {}
+ PDA-pen: !type:ContainerSlot {}
+ - type: ItemSlots
- type: Clothing
quickEquip: false
slots:
diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
index 3f35f6945a..4dd0956442 100644
--- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
+++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_structures.yml
@@ -147,7 +147,7 @@
state: toms
- type: entity
- parent: BasePlaceableInstrument
+ parent: [ BasePlaceableInstrument, ConstructibleMachine]
id: DawInstrument
name: digital audio workstation
description: Cutting edge music technology, straight from the 90s.
@@ -161,9 +161,6 @@
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: Appearance
- type: WiresVisuals
- - type: Construction
- graph: Machine
- node: machine
- type: Wires
BoardName: "DawInstrument"
LayoutId: DawInstrument
diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml
index d1ea5e30c1..7bded8bbbf 100644
--- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml
+++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml
@@ -491,6 +491,10 @@
capacity: 1
soundInsert:
path: /Audio/Weapons/drawbow2.ogg
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
parent: BaseItem
@@ -530,6 +534,10 @@
autoCycle: true
soundInsert:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
parent: BaseItem
diff --git a/Resources/Prototypes/Entities/Objects/Misc/box.yml b/Resources/Prototypes/Entities/Objects/Misc/box.yml
index 42f17b3509..0e654d8ae9 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/box.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/box.yml
@@ -12,3 +12,6 @@
- type: Storage
capacity: 30
size: 10
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml
index 47bfb9f0d0..222e1d6607 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml
@@ -7,6 +7,7 @@
- type: HandheldLight
addPrefix: true
- type: PowerCellSlot
+ - type: ItemSlots
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
@@ -16,6 +17,7 @@
- type: Item
sprite: Objects/Misc/Lights/lights.rsi
size: 20
+ heldPrefix: off
- type: PointLight
netsync: false
enabled: false
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
index 4d76dbdd3b..d9232130c1 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml
@@ -67,6 +67,11 @@
False: {visible: false}
- type: Pullable
- type: AntiRottingContainer
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ entity_storage: !type:Container
+ paper_label: !type:ContainerSlot
- type: entity
id: BodyBag_Folded
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml
index c448d6c7a2..5480298774 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml
@@ -67,3 +67,8 @@
- type: StorageVisualizer
state_open: artifact_container_open
state_closed: artifact_container_door
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ entity_storage: !type:Container
+ paper_label: !type:ContainerSlot
diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml
index bb436edf6c..c19d3b631c 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml
@@ -31,6 +31,7 @@
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
+ - type: ItemSlots
- type: Sprite
sprite: Objects/Tools/flashlight.rsi
netsync: false
diff --git a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml
index fb8e4e4d37..94b6711b3b 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml
@@ -30,6 +30,10 @@
- type: PowerCellSlot
cellSlot:
startingItem: PowerCellMedium
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ cell_slot: !type:ContainerSlot {}
- type: entity
name: extra-bright lantern
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/antimaterial.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/antimaterial.yml
index bc01ce9637..ce891be9a9 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/antimaterial.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/antimaterial.yml
@@ -19,3 +19,7 @@
steps: 2
zeroVisible: false
- type: Appearance
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
index 39904580fa..5678975927 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/toy.yml
@@ -11,6 +11,10 @@
capacity: 30
- type: Sprite
netsync: false
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
# Boxes
- type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/magnum.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/magnum.yml
index 2dfe35663e..89e915f376 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/magnum.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/magnum.yml
@@ -11,6 +11,10 @@
capacity: 6
- type: Sprite
netsync: false
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
id: SpeedLoaderMagnum
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/pistol.yml
index 4fb7b912c9..8e3841ad8c 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/pistol.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/pistol.yml
@@ -12,6 +12,10 @@
- type: Sprite
netsync: false
sprite: Objects/Weapons/Guns/Ammunition/SpeedLoaders/Pistol/pistol_speed_loader.rsi
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
id: SpeedLoaderPistol
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/rifle_light.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/rifle_light.yml
index e3bfbc35e7..846c79da90 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/rifle_light.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/rifle_light.yml
@@ -22,3 +22,7 @@
steps: 6
zeroVisible: false
- type: Appearance
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/toy.yml
index a505992d06..e6fb23a304 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/toy.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/toy.yml
@@ -21,6 +21,10 @@
steps: 7
zeroVisible: false
- type: Appearance
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
id: SpeedLoaderCap
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
index 6a40806850..957aa63004 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
@@ -44,3 +44,7 @@
steps: 4
zeroVisible: true
- type: Appearance
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
index fc26a02171..f00aa20d92 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
@@ -37,14 +37,24 @@
angularDamping: 0
- type: Fixtures
fixtures:
- - shape:
+ - id: projectile
+ shape:
!type:PhysShapeAabb
bounds: "-0.1,-0.1,0.1,0.1"
hard: false
- id: projectile
mask:
- Impassable
- BulletImpassable
+ - &flybyfixture
+ id: fly-by
+ shape: !type:PhysShapeCircle
+ radius: 1.5
+ layer:
+ - Impassable
+ - MidImpassable
+ - HighImpassable
+ - LowImpassable
+ hard: False
- type: Projectile
impactEffect: BulletImpactEffect
damage:
@@ -76,6 +86,7 @@
mask:
- Impassable
- BulletImpassable
+ - *flybyfixture
- type: entity
id: BaseBulletHighVelocity
@@ -142,6 +153,7 @@
mask:
- Impassable
- BulletImpassable
+ - *flybyfixture
- type: Ammo
- type: StaminaDamageOnCollide
damage: 30
@@ -178,6 +190,7 @@
mask:
- Impassable
- Opaque
+ - *flybyfixture
- type: Projectile
# soundHit: Waiting on serv3
damage:
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
index a33e5b5011..5418ecb364 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
@@ -17,12 +17,17 @@
- SemiAuto
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/revolver.ogg
+ - type: ContainerContainer
+ containers:
+ revolver-ammo: !type:Container
- type: RevolverAmmoProvider
whitelist:
tags:
- CartridgeMagnum
proto: CartridgeMagnum
capacity: 7
+ chambers: [ True, True, True, True, True, True, True ]
+ ammoSlots: [ null, null, null, null, null, null, null ]
soundEject:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
soundInsert:
@@ -46,6 +51,8 @@
sprite: Objects/Weapons/Guns/Revolvers/deckard.rsi
- type: RevolverAmmoProvider
capacity: 5
+ chambers: [ True, True, True, True, True ]
+ ammoSlots: [ null, null, null, null, null ]
- type: MagazineVisuals
magState: mag
steps: 4
@@ -64,6 +71,8 @@
sprite: Objects/Weapons/Guns/Revolvers/inspector.rsi
- type: RevolverAmmoProvider
capacity: 6
+ chambers: [ True, True, True, True, True, True ]
+ ammoSlots: [ null, null, null, null, null, null ]
- type: entity
name: Mateba
@@ -75,8 +84,6 @@
sprite: Objects/Weapons/Guns/Revolvers/mateba.rsi
- type: Item
sprite: Objects/Weapons/Guns/Revolvers/mateba.rsi
- - type: RevolverAmmoProvider
- capacity: 7
- type: Gun
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/mateba.ogg
@@ -104,5 +111,10 @@
sprite: Objects/Weapons/Guns/Revolvers/pirate_revolver.rsi
- type: Gun
fireRate: 1
+ - type: ContainerContainer
+ containers:
+ revolver-ammo: !type:Container
- type: RevolverAmmoProvider
capacity: 5
+ chambers: [ True, True, True, True, True ]
+ ammoSlots: [ null, null, null, null, null ]
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
index 4b73e00fd4..a37916deb8 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
@@ -31,6 +31,10 @@
proto: ShellShotgun
soundInsert:
path: /Audio/Weapons/Guns/MagIn/shotgun_insert.ogg
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
name: Bulldog
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
index 7bc77a499e..6baa1e83cd 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
@@ -27,6 +27,10 @@
whitelist:
tags:
- CartridgeLightRifle
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
- type: entity
name: Kardashev-Mosin
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
index 8be9fef539..c08ab11d42 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
@@ -27,3 +27,7 @@
capacity: 1
soundInsert:
path: /Audio/Weapons/Guns/MagIn/shotgun_insert.ogg
+ - type: ContainerContainer
+ containers:
+ ballistic-ammo: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Objects/base_item.yml b/Resources/Prototypes/Entities/Objects/base_item.yml
index 5e90746d13..71372ab368 100644
--- a/Resources/Prototypes/Entities/Objects/base_item.yml
+++ b/Resources/Prototypes/Entities/Objects/base_item.yml
@@ -22,6 +22,7 @@
- type: Physics
bodyType: Dynamic
fixedRotation: false
+ canCollide: false # most entities start asleep.
- type: Fixtures
fixtures:
- shape:
@@ -48,4 +49,8 @@
interfaces:
- key: enum.StorageUiKey.Key
type: StorageBoundUserInterface
+ - type: ContainerContainer
+ containers:
+ storagebase: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
index a4840f2fa6..c2855fb121 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml
@@ -1,6 +1,7 @@
- type: entity
abstract: true
id: ReagentDispenserBase
+ parent: ConstructibleMachine
placement:
mode: SnapgridCenter
components:
@@ -52,3 +53,9 @@
whitelist:
components:
- FitsInDispenser
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ machine_board: !type:Container
+ machine_parts: !type:Container
+ ReagentDispenser-beaker: !type:ContainerSlot
diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
index 7074cbbfd3..9596a58ead 100644
--- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
+++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
@@ -12,9 +12,6 @@
emagPack: ChemDispenserEmaggedInventory
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
index 7581e6c749..3ddafe00bc 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
@@ -85,10 +85,14 @@
interfaces:
- key: enum.WiresUiKey.Key
type: WiresBoundUserInterface
+ - type: Physics
+ canCollide: false
- type: Airtight
fixVacuum: true
+ airBlocked: false
noAirWhenFullyAirBlocked: true
- type: Occluder
+ enabled: false
- type: Construction
graph: Firelock
node: Firelock
@@ -126,6 +130,7 @@
sprite: Structures/Doors/edge_door_hazard.rsi
- type: Airtight
fixVacuum: true
+ airBlocked: false
noAirWhenFullyAirBlocked: false
airBlockedDirection:
- South
@@ -143,3 +148,5 @@
enabled: false
- type: Door
occludes: false
+ - type: Physics
+ canCollide: false
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml
index b4e42fd2ca..861b48fa0f 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door.yml
@@ -33,3 +33,9 @@
components:
- type: Door
state: Open
+ - type: Occluder
+ enabled: false
+ - type: Physics
+ canCollide: false
+ - type: Airtight
+ airBlocked: false
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml
index fb4a9e96d6..31a9348d31 100644
--- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml
+++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml
@@ -66,6 +66,11 @@
key: walls
mode: NoSprite
- type: DoorSignalControl
+ - type: SignalReceiver
+ inputs:
+ Open: []
+ Close: []
+ Toggle: []
- type: InteractionPopup
interactSuccessString: comp-window-knock
messagePerceivedByOthers: comp-window-knock
@@ -127,6 +132,12 @@
- type: Construction
graph: Shutters
node: ShuttersRadiation
+ - type: Occluder
+ enabled: false
+ - type: Physics
+ canCollide: false
+ - type: Airtight
+ airBlocked: false
- type: entity
id: ShuttersWindow
@@ -154,6 +165,10 @@
- type: Construction
graph: Shutters
node: ShuttersWindow
+ - type: Physics
+ canCollide: false
+ - type: Airtight
+ airBlocked: false
# Frame for construction
- type: entity
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml
index 2f30188dad..86efb68868 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml
@@ -25,6 +25,9 @@
- type: PottedPlantHide
- type: SecretStash
secretPartName: the plant
+ - type: ContainerContainer
+ containers:
+ stash: !type:ContainerSlot {}
- type: Pullable
- type: Damageable
damageContainer: Inorganic # The pot. Not the plant. Or is it plastic?
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
index 94f8711e02..b906ce0557 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
@@ -18,6 +18,9 @@
- type: Toilet
- type: SecretStash
secretPartName: the toilet cistern
+ - type: ContainerContainer
+ containers:
+ stash: !type:ContainerSlot {}
- type: SolutionContainerManager
solutions:
toilet:
@@ -28,6 +31,7 @@
graph: Toilet
node: toilet
- type: Appearance
+
- type: entity
id: ToiletDirtyWater
parent: ToiletEmpty
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml
index 9f22ed81e9..d66c499153 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml
@@ -49,3 +49,7 @@
- type: EmitSoundOnUIOpen
sound:
collection: Keyboard
+ - type: ContainerContainer
+ containers:
+ board: !type:Container
+ ents: []
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
index b935b1fc2f..329451bba4 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
@@ -382,6 +382,12 @@
- type: Tag
tags:
- EmagImmune
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ board: !type:Container
+ IdCardConsole-privilegedId: !type:ContainerSlot
+ IdCardConsole-targetId: !type:ContainerSlot
- type: entity
parent: BaseComputer
diff --git a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml
index f944f13a58..aac464e2b9 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml
@@ -43,3 +43,19 @@
- type: ApcPowerReceiver
powerLoad: 1000
- type: ExtensionCableReceiver
+
+- type: entity
+ abstract: true
+ id: ConstructibleMachine
+ components:
+ - type: Machine
+ - type: ContainerContainer
+ containers:
+ machine_board: !type:Container
+ machine_parts: !type:Container
+ - type: Construction
+ graph: Machine
+ node: machine
+ containers:
+ - machine_parts
+ - machine_board
diff --git a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
index ce34ea4a5a..08509f13bd 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
@@ -1,6 +1,6 @@
- type: entity
id: chem_master
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
name: ChemMaster 4000
description: An industrial grade chemical manipulator with pill and bottle production included.
placement:
@@ -60,7 +60,12 @@
- type: Machine
board: ChemMasterMachineCircuitboard
- type: MaterialStorage
- - type: Construction
- graph: Machine
- node: machine
-
+ - type: ContainerContainer
+ containers:
+ machine_board: !type:Container
+ machine_parts: !type:Container
+ ChemMaster-beaker: !type:ContainerSlot
+ - type: ItemSlots
+ - type: SolutionContainerManager
+ solutions:
+ buffer: {}
diff --git a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml
index 9c5be27cec..7784117f29 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml
@@ -1,6 +1,6 @@
- type: entity
id: CloningPod
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
name: cloning pod
description: A Cloning Pod. 50% reliable.
components:
@@ -25,9 +25,6 @@
- MachineMask
layer:
- MachineLayer
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
index 87e213238c..43af5b8ca4 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
@@ -1,5 +1,5 @@
- type: entity
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
id: Autolathe
name: autolathe
description: It produces items using metal and glass.
@@ -34,9 +34,6 @@
- MachineMask
layer:
- MachineLayer
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
@@ -86,7 +83,7 @@
- Ingot
- type: entity
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
id: Protolathe
name: protolathe
description: Converts raw materials into useful objects.
@@ -122,9 +119,6 @@
layer:
- MachineLayer
- type: ResearchClient
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
diff --git a/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml b/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml
index 467cd0fea3..a0822036fa 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml
@@ -1,6 +1,6 @@
- type: entity
id: MedicalScanner
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
name: medical scanner
description: A bulky medical scanner.
components:
@@ -28,9 +28,6 @@
- MachineMask
layer:
- MachineLayer
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
index 25cabeccae..0853198298 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
@@ -42,3 +42,8 @@
drawdepth: SmallObjects
- type: ApcPowerReceiver
powerLoad: 300
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ ReagentGrinder-reagentContainerContainer: !type:ContainerSlot
+ ReagentGrinder-entityContainerContainer: !type:Container
diff --git a/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml b/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml
index 84f88db3e3..70491adbd8 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml
@@ -1,6 +1,6 @@
- type: entity
abstract: true
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
id: SurveillanceCameraRouterBase
name: camera router
description: A surveillance camera router. It routes. Perhaps.
@@ -14,9 +14,6 @@
interfaces:
- key: enum.SurveillanceCameraSetupUiKey.Router
type: SurveillanceCameraSetupBoundUi
- - type: Construction
- graph: Machine
- node: machine
- type: Machine
board: SurveillanceCameraRouterCircuitboard
- type: Sprite
@@ -105,7 +102,7 @@
subnetFrequency: SurveillanceCameraGeneral
- type: entity
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
id: SurveillanceCameraWirelessRouterBase
name: wireless camera router
description: A wireless surveillance camera router. It routes. Perhaps.
@@ -120,9 +117,6 @@
interfaces:
- key: enum.SurveillanceCameraSetupUiKey.Router
type: SurveillanceCameraSetupBoundUi
- - type: Construction
- graph: Machine
- node: machine
- type: Machine
board: SurveillanceCameraWirelessRouterCircuitboard
- type: Sprite
diff --git a/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml
index d5f6c049db..1a96812418 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml
@@ -1,13 +1,10 @@
- type: entity
abstract: true
- parent: BaseStructureDynamic
+ parent: [ BaseStructureDynamic, ConstructibleMachine ]
id: SurveillanceWirelessCameraBase
name: wireless camera
description: A camera. It's watching you. Kinda.
components:
- - type: Construction
- graph: Machine
- node: machine
- type: InteractionOutline
- type: Eye
- type: WirelessNetworkConnection
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml
index 8ffc122857..0e0225a865 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml
@@ -9,6 +9,7 @@
- type: Clickable
- type: InteractionOutline
- type: Physics
+ - type: Fixtures
- type: Transform
anchored: true
- type: Damageable
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
index 0f1fcf5445..2bda18bcee 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml
@@ -15,6 +15,7 @@
damageContainer: Inorganic
damageModifierSet: Metallic
- type: SubFloorHide
+ - type: CollideOnAnchor
- type: PipeAppearance
- type: Anchorable
- type: Rotatable
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
index 2963e17f6f..558fdaec14 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
@@ -15,6 +15,7 @@
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: South
+ - type: CollideOnAnchor
- type: entity
parent: GasUnaryBase
@@ -184,7 +185,7 @@
- enum.LightLayers.Unshaded
- type: entity
- parent: BaseMachinePowered
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
id: BaseGasThermoMachine
name: thermomachine
abstract: true
@@ -207,9 +208,6 @@
- type: ActivatableUI
inHandsOnly: false
key: enum.ThermomachineUiKey.Key
- - type: Construction
- graph: Machine
- node: machine
- type: Wires
BoardName: "Thermomachine"
LayoutId: Thermomachine
@@ -244,10 +242,6 @@
mode: Freezer
- type: Machine
board: ThermomachineFreezerMachineCircuitBoard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
- type: entity
parent: BaseGasThermoMachine
@@ -273,7 +267,3 @@
mode: Heater
- type: Machine
board: ThermomachineHeaterMachineCircuitBoard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
index 9ace34faf5..b276055439 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
@@ -49,6 +49,11 @@
node: broken
- type: Rotatable
- type: Pullable
+ - type: CollideOnAnchor
+ - type: ContainerContainer
+ containers:
+ disposal-tube: !type:Container
+ ents: []
- type: entity
id: DisposalHolder
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
index e017b41bea..744cd5a4a2 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
@@ -70,6 +70,10 @@
interfaces:
- key: enum.DisposalUnitUiKey.Key
type: DisposalUnitBoundUserInterface
+ - type: ContainerContainer
+ containers:
+ disposal-unit: !type:Container
+ ents: []
- type: entity
id: DisposalUnit
diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml
index f430c6d5cd..5f777dff7b 100644
--- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml
@@ -1,6 +1,7 @@
- type: entity
id: Emitter
name: emitter
+ parent: ConstructibleMachine
description: A heavy duty industrial laser. Shoots non-stop when turned on.
placement:
mode: SnapgridCenter
@@ -70,8 +71,5 @@
locked: false
- type: AccessReader
access: [[ "Engineering" ]]
- - type: Construction
- graph: Machine
- node: machine
- type: Machine
board: EmitterCircuitboard
diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml
index 9a659a181f..1b0d6f6673 100644
--- a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml
@@ -151,7 +151,7 @@
supplyRate: 3000
- type: entity
- parent: BaseGenerator
+ parent: [ BaseGenerator, ConstructibleMachine ]
id: GeneratorPlasma
suffix: Plasma, 5kW
components:
@@ -160,21 +160,14 @@
- type: Sprite
sprite: Structures/Power/Generation/portable_generator.rsi
state: portgen0_1
- - type: Construction
- graph: Machine
- node: machine
- type: Wires
BoardName: "GeneratorPlasma"
LayoutId: GeneratorPlasma
- type: Machine
board: GeneratorPlasmaMachineCircuitboard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
- type: entity
- parent: BaseGenerator
+ parent: [ BaseGenerator, ConstructibleMachine ]
id: GeneratorUranium
suffix: Uranium, 15kW
components:
@@ -183,18 +176,11 @@
- type: Sprite
sprite: Structures/Power/Generation/portable_generator.rsi
state: portgen1_1
- - type: Construction
- graph: Machine
- node: machine
- type: Wires
BoardName: "GeneratorUranium"
LayoutId: GeneratorUranium
- type: Machine
board: GeneratorUraniumMachineCircuitboard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
- type: entity
parent: BaseGeneratorWallmount
diff --git a/Resources/Prototypes/Entities/Structures/Power/cables.yml b/Resources/Prototypes/Entities/Structures/Power/cables.yml
index 2bf3f428ea..91ae5d1cfe 100644
--- a/Resources/Prototypes/Entities/Structures/Power/cables.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/cables.yml
@@ -26,6 +26,7 @@
- !type:DoActsBehavior
acts: ["Destruction"]
- type: SubFloorHide
+ - type: CollideOnAnchor
- type: AmbientSound
enabled: false # Leaving as false because 90% of them are set to this.
- type: Appearance
diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml
index 0dbbc9c6a0..eb4c8d8195 100644
--- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml
@@ -38,6 +38,10 @@
- MachineMask
layer:
- HighImpassable
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ charger-slot: !type:ContainerSlot
- type: entity
name: recharger
diff --git a/Resources/Prototypes/Entities/Structures/Power/smes.yml b/Resources/Prototypes/Entities/Structures/Power/smes.yml
index 9328d35934..7376574210 100644
--- a/Resources/Prototypes/Entities/Structures/Power/smes.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/smes.yml
@@ -2,7 +2,7 @@
- type: entity
abstract: true
id: BaseSMES
- parent: BaseMachine
+ parent: [ BaseMachine, ConstructibleMachine ]
name: SMES
description: A high-capacity superconducting magnetic energy storage (SMES) unit.
placement:
@@ -51,18 +51,11 @@
energy: 1.6
color: "#c9c042"
castShadows: false
- - type: Construction
- graph: Machine
- node: machine
- type: Wires
BoardName: "SMES"
LayoutId: SMES
- type: Machine
board: SMESMachineCircuitboard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
- type: StationInfiniteBatteryTarget
# SMES' in use
diff --git a/Resources/Prototypes/Entities/Structures/Power/substation.yml b/Resources/Prototypes/Entities/Structures/Power/substation.yml
index ec67a2dda0..4b00ff4c6c 100644
--- a/Resources/Prototypes/Entities/Structures/Power/substation.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/substation.yml
@@ -2,7 +2,7 @@
- type: entity
abstract: true
id: BaseSubstation
- parent: BaseMachine
+ parent: [ BaseMachine, ConstructibleMachine ]
name: substation
description: Reduces the voltage of electricity put into it.
placement:
@@ -43,9 +43,6 @@
maxChargeRate: 5000
supplyRampTolerance: 5000
supplyRampRate: 1000
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
@@ -73,10 +70,6 @@
LayoutId: Substation
- type: Machine
board: SubstationMachineCircuitboard
- - type: ContainerContainer
- containers:
- machine_parts: !type:Container
- machine_board: !type:Container
- type: StationInfiniteBatteryTarget
# Compact Wall Substation Base
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml
index 219dec801e..7c774a9948 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml
@@ -34,6 +34,7 @@
capacity: 500
- type: Weldable
- type: PlaceableSurface
+ isPlaceable: false # defaults to closed.
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
@@ -61,6 +62,7 @@
containers:
entity_storage: !type:Container
paper_label: !type:ContainerSlot
+ - type: ItemSlots
- type: entity
id: CrateBaseSecure
@@ -97,6 +99,7 @@
capacity: 500
- type: Weldable
- type: PlaceableSurface
+ isPlaceable: false # defaults to closed.
- type: Damageable
damageContainer: Inorganic
damageModifierSet: StrongMetallic
@@ -123,3 +126,10 @@
- type: Construction
graph: CrateSecure
node: cratesecure
+ containers:
+ - entity_storage
+ - type: ContainerContainer
+ containers:
+ entity_storage: !type:Container
+ paper_label: !type:ContainerSlot
+ - type: ItemSlots
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml
index 9ea6adf105..e5736ac0c2 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml
@@ -23,7 +23,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
-
+ containers:
+ - entity_storage
- type: entity
id: CratePlastic
@@ -50,6 +51,8 @@
- type: Construction
graph: CratePlastic
node: crateplastic
+ containers:
+ - entity_storage
- type: entity
id: CrateFreezer
@@ -76,6 +79,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: AntiRottingContainer
- type: entity
@@ -103,6 +108,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateMedical
@@ -129,6 +136,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateRadiation
@@ -156,6 +165,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateInternals
@@ -182,6 +193,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateElectrical
@@ -208,6 +221,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateEngineering
@@ -234,6 +249,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateScience
@@ -260,6 +277,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
- type: entity
id: CrateSurgery
@@ -286,6 +305,8 @@
- type: Construction
graph: CrateGenericSteel
node: crategenericsteel
+ containers:
+ - entity_storage
# Secure Crates
@@ -629,4 +650,6 @@
- type: Construction
graph: CrateLivestock
node: cratelivestock
+ containers:
+ - entity_storage
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml
index d3c32f3b8b..47440c409f 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/extinguisher_cabinet.yml
@@ -31,6 +31,10 @@
- type: ItemCabinetVisuals
openState: open
closedState: closed
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ ItemCabinet: !type:ContainerSlot
placement:
mode: SnapgridCenter
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml
index 14911afb58..b86353c024 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fireaxe_cabinet.yml
@@ -35,6 +35,10 @@
closedState: glass
- type: AccessReader
access: [["Atmospherics"]]
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ ItemCabinet: !type:ContainerSlot
placement:
mode: SnapgridCenter
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml
index f0fb1c63f6..5dfb21d0da 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml
@@ -22,6 +22,11 @@
- type: Construction
graph: SignalSwitchGraph
node: SignalSwitchNode
+ - type: Fixtures
+ - type: SignalTransmitter
+ outputs:
+ On: []
+ Off: []
- type: entity
id: SignalButton
@@ -49,6 +54,10 @@
- type: Construction
graph: SignalButtonGraph
node: SignalButtonNode
+ - type: Fixtures
+ - type: SignalTransmitter
+ outputs:
+ Pressed: []
- type: entity
id: ApcNetSwitch
@@ -79,6 +88,7 @@
- type: Construction
graph: LightSwitchGraph
node: LightSwitchNode
+ - type: Fixtures
- type: entity
id: TwoWayLever
@@ -120,3 +130,9 @@
- type: Construction
graph: LeverGraph
node: LeverNode
+ - type: Fixtures
+ - type: SignalTransmitter
+ outputs:
+ Left: []
+ Right: []
+ Middle: []
diff --git a/Resources/Prototypes/Entities/Structures/hydro_tray.yml b/Resources/Prototypes/Entities/Structures/hydro_tray.yml
index acc70706c8..27150b379e 100644
--- a/Resources/Prototypes/Entities/Structures/hydro_tray.yml
+++ b/Resources/Prototypes/Entities/Structures/hydro_tray.yml
@@ -1,6 +1,6 @@
- type: entity
name: hydroponics tray
- parent: hydroponicsSoil
+ parent: [ hydroponicsSoil, ConstructibleMachine]
id: hydroponicsTray
description: An interstellar-grade space farmplot allowing for rapid growth and selective breeding of crops. Just... keep in mind the space weeds.
components:
@@ -66,9 +66,6 @@
False: { visible: false }
- type: PlantHolder
drawWarnings: true
- - type: Construction
- graph: Machine
- node: machine
- type: Destructible
thresholds:
- trigger:
From e632787490c1c920ca9222ee21dea96ae290002d Mon Sep 17 00:00:00 2001
From: Martin Petkovski <63034378+martin69420@users.noreply.github.com>
Date: Wed, 17 Aug 2022 10:56:53 +1000
Subject: [PATCH 010/109] fixed typos and added descriptions (#10615)
---
.../Devices/Circuitboards/computer.yml | 44 +++---
.../Objects/Materials/Sheets/glass.yml | 3 +
.../Structures/Piping/Disposal/pipes.yml | 12 +-
.../Structures/Power/Generation/solar.yml | 3 +
.../Structures/Wallmounts/Signs/signs.yml | 125 +++++++++++++++---
.../Recipes/Construction/furniture.yml | 2 +-
.../Recipes/Construction/machines.yml | 2 +
.../Recipes/Construction/materials.yml | 3 +
.../Recipes/Construction/utilities.yml | 33 ++++-
.../Prototypes/Recipes/Crafting/crates.yml | 6 +-
10 files changed, 179 insertions(+), 54 deletions(-)
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
index f3544d6c3e..2747070e7c 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
@@ -18,7 +18,7 @@
parent: BaseComputerCircuitboard
id: AlertsComputerCircuitboard
name: alerts computer board
- description: A computer printed circuit board for an alerts computer
+ description: A computer printed circuit board for an alerts computer.
components:
- type: ComputerBoard
prototype: ComputerAlert
@@ -27,7 +27,7 @@
parent: BaseComputerCircuitboard
id: PowerComputerCircuitboard
name: power monitoring computer board
- description: A computer printed circuit board for a power monitoring computer
+ description: A computer printed circuit board for a power monitoring computer.
components:
- type: ComputerBoard
prototype: ComputerPowerMonitoring
@@ -36,7 +36,7 @@
parent: BaseComputerCircuitboard
id: MedicalRecordsComputerCircuitboard
name: medical records computer board
- description: A computer printed circuit board for a medical records computer
+ description: A computer printed circuit board for a medical records computer.
components:
- type: ComputerBoard
prototype: ComputerMedicalRecords
@@ -45,7 +45,7 @@
parent: BaseComputerCircuitboard
id: CriminalRecordsComputerCircuitboard
name: criminal records computer board
- description: A computer printed circuit board for a criminal records computer
+ description: A computer printed circuit board for a criminal records computer.
components:
- type: ComputerBoard
prototype: ComputerCriminalRecords
@@ -54,7 +54,7 @@
parent: BaseComputerCircuitboard
id: SupplyComputerCircuitboard
name: supply ordering computer board
- description: A computer printed circuit board for a supply ordering console
+ description: A computer printed circuit board for a supply ordering console.
components:
- type: ComputerBoard
prototype: ComputerCargoOrders
@@ -65,7 +65,7 @@
parent: BaseComputerCircuitboard
id: SupplyRequestComputerCircuitboard
name: supply request computer board
- description: A computer printed circuit board for a supply request console
+ description: A computer printed circuit board for a supply request console.
components:
- type: ComputerBoard
prototype: ComputerCargoOrders
@@ -74,7 +74,7 @@
parent: BaseComputerCircuitboard
id: SurveillanceCameraMonitorCircuitboard
name: surveillance camera monitor board
- description: A computer printed circuit board for a surveillance camera monitor
+ description: A computer printed circuit board for a surveillance camera monitor.
components:
- type: ComputerBoard
prototype: ComputerSurveillanceCameraMonitor
@@ -83,7 +83,7 @@
parent: BaseComputerCircuitboard
id: SurveillanceWirelessCameraMonitorCircuitboard
name: surveillance wireless camera monitor board
- description: A computer printed circuit board for a surveillance wireless camera monitor
+ description: A computer printed circuit board for a surveillance wireless camera monitor.
components:
- type: ComputerBoard
prototype: ComputerSurveillanceWirelessCameraMonitor
@@ -92,7 +92,7 @@
parent: BaseComputerCircuitboard
id: ComputerTelevisionCircuitboard
name: television board
- description: A computer printed circuit board for a television
+ description: A computer printed circuit board for a television.
components:
- type: ComputerBoard
prototype: ComputerTelevision
@@ -109,7 +109,7 @@
parent: BaseComputerCircuitboard
id: ResearchComputerCircuitboard
name: R&D computer board
- description: A computer printed circuit board for a R&D console
+ description: A computer printed circuit board for a R&D console.
components:
- type: ComputerBoard
prototype: ComputerResearchAndDevelopment
@@ -118,7 +118,7 @@
parent: BaseComputerCircuitboard
id: CrewMonitoringComputerCircuitboard
name: crew monitoring computer board
- description: A computer printed circuit board for a crew monitoring console
+ description: A computer printed circuit board for a crew monitoring console.
components:
- type: ComputerBoard
prototype: ComputerCrewMonitoring
@@ -127,7 +127,7 @@
parent: BaseComputerCircuitboard
id: IDComputerCircuitboard
name: ID card computer board
- description: A computer printed circuit board for an ID card console
+ description: A computer printed circuit board for an ID card console.
components:
- type: ComputerBoard
prototype: ComputerId
@@ -138,7 +138,7 @@
parent: BaseComputerCircuitboard
id: BodyScannerComputerCircuitboard
name: body scanner computer board
- description: A computer printed circuit board for a body scanner console
+ description: A computer printed circuit board for a body scanner console.
components:
- type: ComputerBoard
prototype: computerBodyScanner
@@ -147,7 +147,7 @@
parent: BaseComputerCircuitboard
id: CommsComputerCircuitboard
name: communications computer board
- description: A computer printed circuit board for a communications console
+ description: A computer printed circuit board for a communications console.
components:
- type: ComputerBoard
prototype: ComputerComms
@@ -156,7 +156,7 @@
parent: BaseComputerCircuitboard
id: SyndicateCommsComputerCircuitboard
name: syndicate communications computer board
- description: A computer printed circuit board for a syndicate communications console
+ description: A computer printed circuit board for a syndicate communications console.
components:
- type: ComputerBoard
prototype: SyndicateComputerComms
@@ -173,7 +173,7 @@
parent: BaseComputerCircuitboard
id: SolarControlComputerCircuitboard
name: solar control computer board
- description: A computer printed circuit board for a solar control console
+ description: A computer printed circuit board for a solar control console.
components:
- type: ComputerBoard
prototype: ComputerSolarControl
@@ -182,7 +182,7 @@
parent: BaseComputerCircuitboard
id: SpaceVillainArcadeComputerCircuitboard
name: space villain arcade board
- description: A computer printed circuit board for a space villain arcade cabinet
+ description: A computer printed circuit board for a space villain arcade cabinet.
components:
- type: ComputerBoard
prototype: SpaceVillainArcade
@@ -191,7 +191,7 @@
parent: BaseComputerCircuitboard
id: BlockGameArcadeComputerCircuitboard
name: block game arcade board
- description: A computer printed circuit board for a block game arcade cabinet
+ description: A computer printed circuit board for a block game arcade cabinet.
components:
- type: ComputerBoard
prototype: BlockGameArcade
@@ -200,7 +200,7 @@
parent: BaseComputerCircuitboard
id: ParticleAcceleratorComputerCircuitboard
name: PA control box computer board
- description: A computer printed circuit board for a particle accelerator control box
+ description: A computer printed circuit board for a particle accelerator control box.
components:
- type: ComputerBoard
prototype: ParticleAcceleratorControlBoxUnfinished
@@ -209,7 +209,7 @@
parent: BaseComputerCircuitboard
id: ShuttleConsoleCircuitboard
name: shuttle console board
- description: A computer printed circuit board for a shuttle console
+ description: A computer printed circuit board for a shuttle console.
components:
- type: ComputerBoard
prototype: ComputerShuttle
@@ -218,7 +218,7 @@
parent: BaseComputerCircuitboard
id: SyndicateShuttleConsoleCircuitboard
name: syndicate shuttle console board
- description: A computer printed circuit board for a syndicate shuttle console
+ description: A computer printed circuit board for a syndicate shuttle console.
components:
- type: ComputerBoard
prototype: ComputerShuttleSyndie
@@ -227,7 +227,7 @@
parent: BaseComputerCircuitboard
id: CloningConsoleComputerCircuitboard
name: cloning console computer board
- description: A computer printed circuit board for a cloning console
+ description: A computer printed circuit board for a cloning console.
components:
- type: ComputerBoard
prototype: ComputerCloningConsole
diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
index ff3712ed03..9b7fb8d34c 100644
--- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
+++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml
@@ -89,6 +89,7 @@
parent: SheetGlassBase
id: SheetRGlass
name: reinforced glass
+ description: A reinforced sheet of glass.
suffix: Full
components:
- type: Material
@@ -130,6 +131,7 @@
parent: SheetGlassBase
id: SheetPGlass
name: plasma glass
+ description: A sheet of translucent plasma.
suffix: Full
components:
- type: Material
@@ -168,6 +170,7 @@
parent: SheetGlassBase
id: SheetRPGlass
name: reinforced plasma glass
+ description: A reinforced sheet of translucent plasma.
suffix: Full
components:
- type: Material
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
index b276055439..67bbf0d630 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
@@ -84,7 +84,7 @@
id: DisposalPipe
parent: DisposalPipeBase
name: disposal pipe segment
- description: A huge pipe segment used for constructing disposal systems
+ description: A huge pipe segment used for constructing disposal systems.
components:
- type: Sprite
drawdepth: ThickPipe
@@ -107,7 +107,7 @@
id: DisposalTagger
parent: DisposalPipeBase
name: disposal pipe tagger
- description: A pipe that tags entities for routing
+ description: A pipe that tags entities for routing.
components:
- type: Sprite
drawdepth: ThickPipe
@@ -136,7 +136,7 @@
id: DisposalTrunk
parent: DisposalPipeBase
name: disposal trunk
- description: A pipe trunk used as an entry point for disposal systems
+ description: A pipe trunk used as an entry point for disposal systems.
components:
- type: Sprite
drawdepth: ThickPipe
@@ -166,7 +166,7 @@
id: DisposalRouter
parent: DisposalPipeBase
name: disposal router
- description: A three-way router. Entities with matching tags get routed to the side
+ description: A three-way router. Entities with matching tags get routed to the side.
components:
- type: Sprite
drawdepth: ThickPipe
@@ -206,6 +206,7 @@
- type: entity
id: DisposalRouterFlipped
+ description: A three-way router. Entities with matching tags get routed to the side.
parent: DisposalRouter
suffix: flipped
components:
@@ -240,7 +241,7 @@
id: DisposalJunction
parent: DisposalPipeBase
name: disposal junction
- description: A three-way junction. The arrow indicates where items exit
+ description: A three-way junction. The arrow indicates where items exit.
components:
- type: Sprite
drawdepth: ThickPipe
@@ -275,6 +276,7 @@
- type: entity
id: DisposalJunctionFlipped
+ description: A three-way junction. The arrow indicates where items exit.
parent: DisposalJunction
suffix: flipped
components:
diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml
index 5e5af7e8b4..f34edd5fe5 100644
--- a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml
+++ b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml
@@ -43,6 +43,7 @@
id: SolarPanel
parent: SolarPanelBasePhysSprite
name: solar panel
+ description: A solar panel that generates power.
components:
- type: PowerSupplier
supplyRampTolerance: 500
@@ -72,6 +73,7 @@
id: SolarPanelBroken
parent: SolarPanelBasePhysSprite
name: solar panel
+ description: A broken solar panel.
suffix: Broken
components:
- type: Sprite
@@ -146,6 +148,7 @@
- type: entity
id: SolarTracker
name: solar tracker
+ description: A solar tracker. Tracks the nearest star.
placement:
mode: SnapgridCenter
components:
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml
index 629903f70e..4ac9854722 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/signs.yml
@@ -52,8 +52,8 @@
description: A direction sign, pointing out which way the chemistry lab is.
components:
- type: Sprite
- state: direction_chemistry
-
+ state: direction_chemistry
+
- type: entity
parent: BaseSign
id: SignDirectionalCryo
@@ -80,7 +80,7 @@
components:
- type: Sprite
state: direction_eng
-
+
- type: entity
parent: BaseSign
id: SignDirectionalEvac
@@ -88,8 +88,8 @@
description: A direction sign, pointing out which way evac is.
components:
- type: Sprite
- state: direction_evac
-
+ state: direction_evac
+
- type: entity
parent: BaseSign
id: SignDirectionalFood
@@ -106,7 +106,7 @@
description: A direction sign, pointing out which way the gravity generator is.
components:
- type: Sprite
- state: direction_gravity
+ state: direction_gravity
- type: entity
parent: BaseSign
@@ -125,7 +125,7 @@
components:
- type: Sprite
state: direction_hydro
-
+
- type: entity
parent: BaseSign
id: SignDirectionalJanitor
@@ -134,7 +134,7 @@
components:
- type: Sprite
state: direction_janitor
-
+
- type: entity
parent: BaseSign
id: SignDirectionalLibrary
@@ -197,7 +197,7 @@
components:
- type: Sprite
state: direction_supply
-
+
- type: entity
parent: BaseSign
id: SignDirectionalWash
@@ -208,12 +208,12 @@
state: direction_wash
# Regular Signs
-# Descriptions intentionally left empty.
- type: entity
parent: BaseSign
id: SignAi
name: ai sign
+ description: A sign, indicating an AI is present.
components:
- type: Sprite
state: ai
@@ -222,6 +222,7 @@
parent: BaseSign
id: SignArmory
name: armory sign
+ description: A sign indicating the armory.
components:
- type: Sprite
state: armory
@@ -230,6 +231,7 @@
parent: BaseSign
id: SignToolStorage
name: tool storage sign
+ description: A sign indicating the tool storage room.
components:
- type: Sprite
state: ass
@@ -238,6 +240,7 @@
parent: BaseSign
id: SignAnomaly
name: anomaly lab sign
+ description: A sign indicating the anomaly lab.
components:
- type: Sprite
state: anomaly
@@ -246,6 +249,7 @@
parent: BaseSign
id: SignAtmos
name: atmos sign
+ description: A sign indicating the atmospherics area.
components:
- type: Sprite
state: atmos
@@ -254,14 +258,16 @@
parent: BaseSign
id: SignAtmosMinsky
name: atmospherics sign
+ description: A sign indicating the atmospherics area.
components:
- type: Sprite
state: atmominsky
-
+
- type: entity
parent: BaseSign
id: SignBar
name: bar sign
+ description: A sign indicating the bar.
components:
- type: Sprite
state: bar
@@ -270,6 +276,7 @@
parent: BaseSign
id: SignBio
name: bio sign
+ description: A sign indicating the biology lab.
components:
- type: Sprite
state: bio
@@ -278,14 +285,16 @@
parent: BaseSign
id: SignBiohazard
name: biohazard sign
+ description: A sign indicating a biohazard.
components:
- type: Sprite
state: biohazard
-
+
- type: entity
parent: BaseSign
id: SignBridge
name: bridge sign
+ description: A sign indicating the bridge.
components:
- type: Sprite
state: bridge
@@ -294,6 +303,7 @@
parent: BaseSign
id: SignCanisters
name: canisters sign
+ description: A sign warning the viewer about pressurised canisters.
components:
- type: Sprite
state: canisters
@@ -302,6 +312,7 @@
parent: BaseSign
id: SignCargo
name: cargo sign
+ description: A sign indicating the cargo area.
components:
- type: Sprite
state: cargo
@@ -310,14 +321,16 @@
parent: BaseSign
id: SignCargoDock
name: cargo dock sign
+ description: A sign indicating a cargo dock.
components:
- type: Sprite
state: cargo_dock
-
+
- type: entity
parent: BaseSign
id: SignChapel
name: chapel sign
+ description: A sign indicating the chapel.
components:
- type: Sprite
state: chapel
@@ -326,6 +339,7 @@
parent: BaseSign
id: SignChem
name: chemistry sign
+ description: A sign indicating the chemistry lab.
components:
- type: Sprite
state: chem
@@ -334,6 +348,7 @@
parent: BaseSign
id: SignChemistry1
name: chemistry sign
+ description: A sign indicating the chemistry lab.
components:
- type: Sprite
state: chemistry1
@@ -342,6 +357,7 @@
parent: BaseSign
id: SignChemistry2
name: chemistry sign
+ description: A sign indicating the chemistry lab.
components:
- type: Sprite
state: chemistry2
@@ -350,6 +366,7 @@
parent: BaseSign
id: SignCloning
name: cloning sign
+ description: A sign indicating the cloning lab.
components:
- type: Sprite
state: cloning
@@ -358,6 +375,7 @@
parent: BaseSign
id: SignConference
name: conference room sign
+ description: A sign indicating the conference room.
components:
- type: Sprite
state: conference_room
@@ -366,7 +384,7 @@
parent: BaseSign
id: SignDisposalSpace
name: disposal sign
- description: "Disposal: Leads to space"
+ description: A sign indicating a disposal area.
components:
- type: Sprite
state: deathsposal
@@ -375,14 +393,16 @@
parent: BaseSign
id: SignDoors
name: doors sign
+ description: A sign indicating doors.
components:
- type: Sprite
state: doors
-
+
- type: entity
parent: BaseSign
id: SignDrones
name: drones sign
+ description: A sign indicating drones.
components:
- type: Sprite
state: drones
@@ -391,6 +411,7 @@
parent: BaseSign
id: SignEngine
name: engine sign
+ description: A sign indicating the engine room.
components:
- type: Sprite
state: engine
@@ -399,6 +420,7 @@
parent: BaseSign
id: SignEngineering
name: engineering sign
+ description: A sign indicating the engineering area.
components:
- type: Sprite
state: eng
@@ -407,6 +429,7 @@
parent: BaseSign
id: SignEscapePods
name: escape pods sign
+ description: A sign indicating the escape pods.
components:
- type: Sprite
state: pods
@@ -415,6 +438,7 @@
parent: BaseSign
id: SignEVA
name: EVA sign
+ description: A sign indicating an EVA area. EVA equipment may be required beyond this point.
components:
- type: Sprite
state: eva
@@ -423,6 +447,7 @@
parent: BaseSign
id: SignElectrical
name: electrical sign
+ description: A sign indicating an electrical hazard.
components:
- type: Sprite
state: electrical
@@ -431,6 +456,7 @@
parent: BaseSign
id: SignExamroom
name: examination room sign
+ description: A sign indicating a medical examination room.
components:
- type: Sprite
state: examroom
@@ -439,6 +465,7 @@
parent: BaseSign
id: SignFire
name: fire sign
+ description: A sign indicating a fire hazard.
components:
- type: Sprite
state: fire
@@ -447,6 +474,7 @@
parent: BaseSign
id: SignGravity
name: gravity sign
+ description: A sign indicating the gravity generator.
components:
- type: Sprite
state: gravi
@@ -455,6 +483,7 @@
parent: BaseSign
id: SignHead
name: head sign
+ description: A sign with a hat on it.
components:
- type: Sprite
state: commander
@@ -463,6 +492,7 @@
parent: BaseSign
id: SignHydro1
name: hydro sign
+ description: A sign indicating a hydroponics area.
components:
- type: Sprite
state: hydro1
@@ -471,6 +501,7 @@
parent: BaseSign
id: SignHydro2
name: hydro sign
+ description: A sign indicating a hydroponics area.
components:
- type: Sprite
state: hydro2
@@ -479,6 +510,7 @@
parent: BaseSign
id: SignHydro3
name: hydro sign
+ description: A sign indicating a hydroponics area.
components:
- type: Sprite
state: hydro3
@@ -487,6 +519,7 @@
parent: BaseSign
id: SignInterrogation
name: interrogation sign
+ description: A sign indicating an interrogation room.
components:
- type: Sprite
state: interrogation
@@ -495,6 +528,7 @@
parent: BaseSign
id: SignLibrary
name: library sign
+ description: A sign indicating the library.
components:
- type: Sprite
state: biblio
@@ -503,6 +537,7 @@
parent: BaseSign
id: SignMail
name: mail sign
+ description: A sign indicating mail.
components:
- type: Sprite
state: mail
@@ -511,14 +546,16 @@
parent: BaseSign
id: SignMedical
name: medbay sign
+ description: A sign indicating the medical bay.
components:
- type: Sprite
state: medbay
-
+
- type: entity
parent: BaseSign
id: SignMinerDock
name: miner dock sign
+ description: A sign indicating the miner dock.
components:
- type: Sprite
state: miner_dock
@@ -527,6 +564,7 @@
parent: BaseSign
id: SignMorgue
name: morgue sign
+ description: A sign indicating the morgue.
components:
- type: Sprite
state: morgue
@@ -535,6 +573,7 @@
parent: BaseSign
id: SignNosmoking
name: nosmoking sign
+ description: A sign indicating that smoking is not allowed in the vicinity.
components:
- type: Sprite
state: nosmoking
@@ -543,6 +582,7 @@
parent: BaseSign
id: SignPrison
name: prison sign
+ description: A sign indicating the prison.
components:
- type: Sprite
state: prison
@@ -551,14 +591,16 @@
parent: BaseSign
id: SignRND
name: research and development sign
+ description: A sign indicating the research and development lab.
components:
- type: Sprite
state: rnd
-
+
- type: entity
parent: BaseSign
id: SignRobo
name: robo sign
+ description: A sign indicating the robotics lab.
components:
- type: Sprite
state: robo
@@ -567,6 +609,7 @@
parent: BaseSign
id: SignScience
name: science sign
+ description: A sign indicating the science area.
components:
- type: Sprite
state: sci
@@ -575,6 +618,7 @@
parent: BaseSign
id: SignScience1
name: science sign
+ description: A sign indicating the science area.
components:
- type: Sprite
state: science1
@@ -583,6 +627,7 @@
parent: BaseSign
id: SignScience2
name: science sign
+ description: A sign indicating the science area.
components:
- type: Sprite
state: science2
@@ -591,6 +636,7 @@
parent: BaseSign
id: SignShield
name: shield sign
+ description: A sign with a shield.
components:
- type: Sprite
state: shield
@@ -599,6 +645,7 @@
parent: BaseSign
id: SignShipDock
name: docking sign
+ description: A sign indicating the ship docking area.
components:
- type: Sprite
state: dock
@@ -607,6 +654,7 @@
parent: BaseSign
id: SignSpace
name: space sign
+ description: A sign warning that the area ahead is nothing but cold, empty space.
components:
- type: Sprite
state: space
@@ -615,6 +663,7 @@
parent: BaseSign
id: SignSurgery
name: surgery sign
+ description: A sign indicating the operating theater.
components:
- type: Sprite
state: surgery
@@ -623,6 +672,7 @@
parent: BaseSign
id: SignTelecomms
name: telecomms sign
+ description: A sign indicating the telecommunications room.
components:
- type: Sprite
state: telecoms
@@ -631,6 +681,7 @@
parent: BaseSign
id: SignToxins
name: toxins sign
+ description: A sign indicating the toxin lab.
components:
- type: Sprite
state: toxins
@@ -639,6 +690,7 @@
parent: BaseSign
id: SignToxins2
name: toxins sign
+ description: A sign indicating the toxin lab.
components:
- type: Sprite
state: toxins2
@@ -647,6 +699,7 @@
parent: BaseSign
id: SignVirology
name: virology sign
+ description: A sign indicating the virology lab.
components:
- type: Sprite
state: virology
@@ -655,6 +708,7 @@
parent: BaseSign
id: SignCorrosives
name: corrosives warning sign
+ description: A sign indicating a corrosive materials hazard.
components:
- type: Sprite
state: corrosives
@@ -663,6 +717,7 @@
parent: BaseSign
id: SignCryogenics
name: cryogenics warning sign
+ description: A sign indicating a cryogenic materials hazard. Bring a jacket!
components:
- type: Sprite
state: cryogenics
@@ -671,6 +726,7 @@
parent: BaseSign
id: SignDanger
name: danger warning sign
+ description: A sign warning against some danger.
components:
- type: Sprite
state: danger
@@ -679,6 +735,7 @@
parent: BaseSign
id: SignExplosives
name: explosives warning sign
+ description: A sign indicating an explosive materials hazard.
components:
- type: Sprite
state: explosives
@@ -687,6 +744,7 @@
parent: BaseSign
id: SignFlammable
name: flammable warning sign
+ description: A sign indicating a flammable materials hazard.
components:
- type: Sprite
state: flammable
@@ -695,14 +753,16 @@
parent: BaseSign
id: SignLaser
name: laser warning sign
+ description: A sign indicating a laser hazard.
components:
- type: Sprite
state: laser
-
+
- type: entity
parent: BaseSign
id: SignMagnetics
name: magnetics warning sign
+ description: A sign indicating a magnetic materials hazard.
components:
- type: Sprite
state: magnetics
@@ -711,6 +771,7 @@
parent: BaseSign
id: SignMemetic
name: memetic warning sign
+ description: A sign indicating a memetic hazard.
components:
- type: Sprite
state: memetic
@@ -719,6 +780,7 @@
parent: BaseSign
id: SignSecure
name: secure sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: secure
@@ -727,14 +789,16 @@
parent: BaseSign
id: SignSecurearea
name: secure area sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: securearea
-
+
- type: entity
parent: BaseSign
id: SignShock
name: shock sign
+ description: A sign indicating an electical hazard.
components:
- type: Sprite
state: shock
@@ -743,6 +807,7 @@
parent: BaseSign
id: SignOptical
name: optical warning sign
+ description: A sign indicating against optical radiation.
components:
- type: Sprite
state: optical
@@ -751,6 +816,7 @@
parent: BaseSign
id: SignOxidants
name: oxidants warning sign
+ description: A sign indicating against oxidzing agents.
components:
- type: Sprite
state: oxidants
@@ -759,6 +825,7 @@
parent: BaseSign
id: SignRadiation
name: radiation warning sign
+ description: A sign indicating against ionizing radiation.
components:
- type: Sprite
state: radiation
@@ -767,6 +834,7 @@
parent: BaseSign
id: SignXenobio
name: xenobio sign
+ description: A sign indicating the xenobiology lab.
components:
- type: Sprite
state: xenobio
@@ -775,6 +843,7 @@
parent: BaseSign
id: SignXenobio2
name: xenobio sign
+ description: A sign indicating the xenobiology lab.
components:
- type: Sprite
state: xenobio2
@@ -783,6 +852,7 @@
parent: BaseSign
id: SignXenolab
name: xenolab sign
+ description: A sign indicating the xenobiology lab.
components:
- type: Sprite
state: xenolab
@@ -791,6 +861,7 @@
parent: BaseSign
id: SignZomlab
name: zombie lab sign
+ description: A sign indicating the zombie lab.
components:
- type: Sprite
state: zomlab
@@ -799,6 +870,7 @@
parent: BaseSign
id: SignSecureMedRed
name: red secure sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: medium_secure_red
@@ -807,6 +879,7 @@
parent: BaseSign
id: SignSecureSmall
name: small secure sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: small_secure
@@ -815,6 +888,7 @@
parent: BaseSign
id: SignSecureSmallRed
name: small red secure sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: small_secure_red
@@ -823,6 +897,7 @@
parent: BaseSign
id: SignBlankMed
name: blank sign
+ description: A blank sign.
components:
- type: Sprite
state: medium_blank
@@ -831,6 +906,7 @@
parent: BaseSign
id: SignMagneticsMed
name: magnetics sign
+ description: A sign indicating the use of magnets.
components:
- type: Sprite
state: medium_magnetics
@@ -839,6 +915,7 @@
parent: BaseSign
id: SignDangerMed
name: danger sign
+ description: A sign warning against some form of danger.
components:
- type: Sprite
state: medium_danger
@@ -847,6 +924,7 @@
parent: BaseSign
id: ExplosivesSignMed
name: explosives sign
+ description: A sign indicating explosive materials.
components:
- type: Sprite
state: medium_explosives
@@ -855,6 +933,7 @@
parent: BaseSign
id: SignCryogenicsMed
name: cryogenics sign
+ description: A sign indicating cryogenic materials.
components:
- type: Sprite
state: medium_cryogenics
@@ -863,6 +942,7 @@
parent: BaseSign
id: SignElectricalMed
name: electrical sign
+ description: A sign indicating an electrical hazard.
components:
- type: Sprite
state: medium_electrical
@@ -871,6 +951,7 @@
parent: BaseSign
id: SignBiohazardMed
name: biohazard sign
+ description: A sign indicating a biohazard.
components:
- type: Sprite
state: medium_biohazard
@@ -879,6 +960,7 @@
parent: BaseSign
id: SignRadiationMed
name: radiation sign
+ description: A sign warning against ionizing radiation.
components:
- type: Sprite
state: medium_radiation
@@ -887,6 +969,7 @@
parent: BaseSign
id: SignFlammableMed
name: flammable sign #when flammable component is done, this sign needs it
+ description: A sign indicating flammable materials.
components:
- type: Sprite
state: medium_flammable
@@ -895,6 +978,7 @@
parent: BaseSign
id: SignLaserMed
name: laser sign
+ description: A sign indicating a laser hazard.
components:
- type: Sprite
state: medium_laser
@@ -903,6 +987,7 @@
parent: BaseSign
id: SignSecureMed
name: secure sign
+ description: A sign indicating that the area ahead is a secure area.
components:
- type: Sprite
state: medium_secure
@@ -964,7 +1049,7 @@
components:
- type: Sprite
state: atmos_plasma
-
+
- type: entity
parent: BaseSign
id: WarningTritium
diff --git a/Resources/Prototypes/Recipes/Construction/furniture.yml b/Resources/Prototypes/Recipes/Construction/furniture.yml
index 4976861d80..d3427fc18d 100644
--- a/Resources/Prototypes/Recipes/Construction/furniture.yml
+++ b/Resources/Prototypes/Recipes/Construction/furniture.yml
@@ -314,7 +314,7 @@
startNode: start
targetNode: toilet
category: construction-category-furniture
- description: A human excrement flushing apparatus
+ description: A human excrement flushing apparatus.
icon:
sprite: Structures/Furniture/toilet.rsi
state: closed_toilet_seat_up
diff --git a/Resources/Prototypes/Recipes/Construction/machines.yml b/Resources/Prototypes/Recipes/Construction/machines.yml
index 1154e25075..1e96810fbf 100644
--- a/Resources/Prototypes/Recipes/Construction/machines.yml
+++ b/Resources/Prototypes/Recipes/Construction/machines.yml
@@ -5,6 +5,7 @@
startNode: start
targetNode: computer
category: construction-category-machines
+ description: A frame used to construct anything with a computer circuitboard.
placementMode: SnapgridCenter
canBuildInImpassable: false
icon:
@@ -13,6 +14,7 @@
- type: construction
name: machine frame
+ description: A machine under construction. Needs more parts.
id: MachineFrame
graph: Machine
startNode: start
diff --git a/Resources/Prototypes/Recipes/Construction/materials.yml b/Resources/Prototypes/Recipes/Construction/materials.yml
index 5bcb56a6b9..2e86ca7bca 100644
--- a/Resources/Prototypes/Recipes/Construction/materials.yml
+++ b/Resources/Prototypes/Recipes/Construction/materials.yml
@@ -11,6 +11,7 @@
- type: construction
name: reinforced glass
+ description: A reinforced sheet of glass.
id: SheetRGlass
graph: Glass
startNode: start
@@ -21,6 +22,7 @@
- type: construction
name: plasma glass
+ description: A sheet of translucent plasma.
id: SheetPGlass
graph: Glass
startNode: start
@@ -31,6 +33,7 @@
- type: construction
name: reinforced plasma glass
+ description: A reinforced sheet of translucent plasma.
id: SheetRPGlass
graph: Glass
startNode: start
diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml
index 4d4290297c..bd7c744a34 100644
--- a/Resources/Prototypes/Recipes/Construction/utilities.yml
+++ b/Resources/Prototypes/Recipes/Construction/utilities.yml
@@ -82,7 +82,7 @@
startNode: start
targetNode: substation
category: construction-category-utilities
- description: "A wallmount substation for compact spaces. Make sure to place cable under before building the wall."
+ description: "A wallmount substation for compact spaces. Make sure to place cable underneath before building the wall."
icon:
sprite: Structures/Power/substation.rsi
state: substation_wall
@@ -97,7 +97,7 @@
startNode: start
targetNode: generator
category: construction-category-utilities
- description: "A wallmount generator for compact spaces. Make sure to place cable under before building the wall."
+ description: "A wallmount generator for compact spaces. Make sure to place cable underneath before building the wall."
icon:
sprite: Structures/Power/Generation/wallmount_generator.rsi
state: panel
@@ -112,7 +112,7 @@
startNode: start
targetNode: APU
category: construction-category-utilities
- description: "A wallmount APU for compact shuttles. Make sure to place cable under before building the wall."
+ description: "A wallmount APU for compact shuttles. Make sure to place cable underneath before building the wall."
icon:
sprite: Structures/Power/Generation/wallmount_generator.rsi
state: panel
@@ -123,6 +123,7 @@
# DISPOSALS
- type: construction
name: disposal unit
+ description: A pneumatic waste disposal unit.
id: DisposalUnit
graph: DisposalMachine
startNode: start
@@ -136,6 +137,7 @@
- type: construction
name: mailing unit
+ description: A pneumatic mail delivery unit.
id: MailingUnit
graph: DisposalMachine
startNode: start
@@ -150,6 +152,7 @@
- type: construction
name: disposal pipe
id: DisposalPipe
+ description: A huge pipe segment used for constructing disposal systems.
graph: DisposalPipe
startNode: start
targetNode: pipe
@@ -162,6 +165,7 @@
- type: construction
name: disposal tagger
+ description: A pipe that tags entities for routing.
id: DisposalTagger
graph: DisposalPipe
startNode: start
@@ -175,6 +179,7 @@
- type: construction
name: disposal trunk
+ description: A pipe trunk used as an entry point for disposal systems.
id: DisposalTrunk
graph: DisposalPipe
startNode: start
@@ -188,6 +193,7 @@
- type: construction
name: disposal router
+ description: A three-way router. Entities with matching tags get routed to the side.
id: DisposalRouter
graph: DisposalPipe
startNode: start
@@ -201,6 +207,7 @@
- type: construction
name: disposal junction
+ description: A three-way junction. The arrow indicates where items exit.
id: DisposalJunction
graph: DisposalPipe
startNode: start
@@ -214,6 +221,7 @@
- type: construction
name: disposal Y junction
+ description: A three-way junction with another exit point.
id: DisposalYJunction
graph: DisposalPipe
startNode: start
@@ -227,6 +235,7 @@
- type: construction
name: disposal bend
+ description: A tube bent at a 90 degree angle.
id: DisposalBend
graph: DisposalPipe
startNode: start
@@ -279,6 +288,7 @@
- type: construction
name: gas pipe half
id: GasPipeHalf
+ description: Half of a gas pipe. No skateboards.
graph: GasPipe
startNode: start
targetNode: half
@@ -292,6 +302,7 @@
- type: construction
name: gas pipe straight
id: GasPipeStraight
+ description: A straight pipe segment.
graph: GasPipe
startNode: start
targetNode: straight
@@ -305,6 +316,7 @@
- type: construction
name: gas pipe bend
id: GasPipeBend
+ description: A pipe segment bent at a 90 degree angle.
graph: GasPipe
startNode: start
targetNode: bend
@@ -318,6 +330,7 @@
- type: construction
name: gas pipe T junction
id: GasPipeTJunction
+ description: A pipe segment with a T junction.
graph: GasPipe
startNode: start
targetNode: tjunction
@@ -331,6 +344,7 @@
- type: construction
name: gas pipe fourway
id: GasPipeFourway
+ description: A pipe segment with a fourway junction.
graph: GasPipe
startNode: start
targetNode: fourway
@@ -344,6 +358,7 @@
# ATMOS UNARY
- type: construction
name: air vent
+ description: Pumps gas into the room.
id: GasVentPump
graph: GasUnary
startNode: start
@@ -359,6 +374,7 @@
- type: construction
name: passive vent
+ description: Unpowered vent that equalises gases on both sides.
id: GasPassiveVent
graph: GasUnary
startNode: start
@@ -374,6 +390,7 @@
- type: construction
name: air scrubber
+ description: Sucks gas into connected pipes.
id: GasVentScrubber
graph: GasUnary
startNode: start
@@ -389,6 +406,7 @@
- type: construction
name: air injector
+ description: Injects air into the atmosphere.
id: GasOutletInjector
graph: GasUnary
startNode: start
@@ -406,6 +424,7 @@
- type: construction
name: gas pump
id: GasPressurePump
+ description: A pump that moves gas by pressure.
graph: GasBinary
startNode: start
targetNode: pressurepump
@@ -420,6 +439,7 @@
- type: construction
name: volumetric gas pump
+ description: A pump that moves gas by volume.
id: GasVolumePump
graph: GasBinary
startNode: start
@@ -436,6 +456,7 @@
- type: construction
id: GasPassiveGate
name: passive gate
+ description: A one-way air valve that does not require power.
graph: GasBinary
startNode: start
targetNode: passivegate
@@ -451,6 +472,7 @@
- type: construction
id: GasValve
name: manual valve
+ description: A pipe with a valve that can be used to disable the flow of gas through it.
graph: GasBinary
startNode: start
targetNode: valve
@@ -466,6 +488,7 @@
- type: construction
id: GasPort
name: connector port
+ description: For connecting portable devices related to atmospherics control.
graph: GasBinary
startNode: start
targetNode: port
@@ -481,6 +504,7 @@
- type: construction
id: GasDualPortVentPump
name: dual-port air vent
+ description: Has a valve and a pump attached to it. There are two ports, one is an input for releasing air, the other is an output when siphoning.
graph: GasBinary
startNode: start
targetNode: dualportventpump
@@ -497,6 +521,7 @@
- type: construction
id: GasFilter
name: gas filter
+ description: Very useful for filtering gases.
graph: GasTrinary
startNode: start
targetNode: filter
@@ -512,6 +537,7 @@
- type: construction
id: GasMixer
name: gas mixer
+ description: Very useful for mixing gases.
graph: GasTrinary
startNode: start
targetNode: mixer
@@ -527,6 +553,7 @@
- type: construction
id: PressureControlledValve
name: pneumatic valve
+ description: Valve controlled by pressure.
graph: GasTrinary
startNode: start
targetNode: pneumaticvalve
diff --git a/Resources/Prototypes/Recipes/Crafting/crates.yml b/Resources/Prototypes/Recipes/Crafting/crates.yml
index dcfce00400..96d45eb642 100644
--- a/Resources/Prototypes/Recipes/Crafting/crates.yml
+++ b/Resources/Prototypes/Recipes/Crafting/crates.yml
@@ -5,7 +5,7 @@
startNode: start
targetNode: cratelivestock
category: construction-category-storage
- description: "A wooden crate for holding livestock"
+ description: A wooden crate for holding livestock.
icon: Structures/Storage/Crates/livestock.rsi/livestockcrate.png
objectType: Structure
@@ -16,7 +16,7 @@
startNode: start
targetNode: crategenericsteel
category: construction-category-storage
- description: "A metal crate for storing things"
+ description: A metal crate for storing things.
icon: Structures/Storage/Crates/generic.rsi/crate_icon.png
objectType: Structure
@@ -27,6 +27,6 @@
startNode: start
targetNode: crateplastic
category: construction-category-storage
- description: "A plastic crate for storing things"
+ description: A plastic crate for storing things.
icon: Structures/Storage/Crates/plastic.rsi/plasticcrate_icon.png
objectType: Structure
From 46cb3c477c2739abe97789ffe3d9f1679a68f866 Mon Sep 17 00:00:00 2001
From: PJBot
Date: Tue, 16 Aug 2022 20:57:57 -0400
Subject: [PATCH 011/109] Automatic changelog update
---
Resources/Changelog/Changelog.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index 3ffd9f771e..04f8b6e077 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,10 +1,4 @@
Entries:
-- author: youarereadingthis
- changes:
- - {message: Removed the shuttle powering crate from the cargo ordering console.,
- type: Remove}
- id: 1665
- time: '2022-06-06T08:34:07.0000000+00:00'
- author: Rane
changes:
- {message: Added mute toxin., type: Add}
@@ -2933,3 +2927,9 @@ Entries:
- {message: Fixed missing death sprite for holocarp and magicarp, type: Fix}
id: 2164
time: '2022-08-16T13:41:26.0000000+00:00'
+- author: martin69420
+ changes:
+ - {message: 'Added descriptions to many entities, including most signs.', type: Add}
+ - {message: Fixed typos on a few descriptions., type: Tweak}
+ id: 2165
+ time: '2022-08-17T00:56:54.0000000+00:00'
From 6cb7bbaf73928b5c157818bfff5d485b1d87aeba Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Wed, 17 Aug 2022 14:17:32 +1200
Subject: [PATCH 012/109] Fix disposal container names (#10646)
---
.../Tube/Components/DisposalBendComponent.cs | 4 ++-
.../Tube/Components/DisposalEntryComponent.cs | 1 +
.../Components/DisposalJunctionComponent.cs | 4 ++-
.../Components/DisposalRouterComponent.cs | 2 ++
.../Components/DisposalTaggerComponent.cs | 2 ++
.../Components/DisposalTransitComponent.cs | 4 ++-
.../Tube/Components/DisposalTubeComponent.cs | 2 +-
.../Components/SharedDisposalUnitComponent.cs | 2 +-
.../Structures/Piping/Disposal/pipes.yml | 33 +++++++++++++++++--
.../Structures/Piping/Disposal/units.yml | 3 +-
10 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs
index fae43d82a2..3ef3cf5b2d 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs
@@ -1,4 +1,4 @@
-using Content.Server.Disposal.Unit.Components;
+using Content.Server.Disposal.Unit.Components;
namespace Content.Server.Disposal.Tube.Components
{
@@ -7,6 +7,8 @@ namespace Content.Server.Disposal.Tube.Components
[ComponentReference(typeof(DisposalTubeComponent))]
public sealed class DisposalBendComponent : DisposalTubeComponent
{
+ public override string ContainerId => "DisposalBend";
+
[DataField("sideDegrees")]
private int _sideDegrees = -90;
diff --git a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs
index dbc65ab25d..e6c3fcf8e2 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs
@@ -13,6 +13,7 @@ namespace Content.Server.Disposal.Tube.Components
[Dependency] private readonly IEntityManager _entMan = default!;
private const string HolderPrototypeId = "DisposalHolder";
+ public override string ContainerId => "DisposalEntry";
public bool TryInsert(DisposalUnitComponent from, IEnumerable? tags = default)
{
diff --git a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs
index 93c5d965fe..ba6276c028 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using Content.Server.Disposal.Unit.Components;
using Robust.Shared.Random;
@@ -10,6 +10,8 @@ namespace Content.Server.Disposal.Tube.Components
[ComponentReference(typeof(DisposalTubeComponent))]
public class DisposalJunctionComponent : DisposalTubeComponent
{
+ public override string ContainerId => "DisposalJunction";
+
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IRobustRandom _random = default!;
diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
index d3294693fd..550b5e94d6 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
@@ -14,6 +14,8 @@ namespace Content.Server.Disposal.Tube.Components
[ComponentReference(typeof(DisposalTubeComponent))]
public sealed class DisposalRouterComponent : DisposalJunctionComponent
{
+ public override string ContainerId => "DisposalRouter";
+
[Dependency] private readonly IEntityManager _entMan = default!;
[ViewVariables]
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
index aab15f299a..bdef8a8b5d 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
@@ -15,6 +15,8 @@ namespace Content.Server.Disposal.Tube.Components
{
[Dependency] private readonly IEntityManager _entMan = default!;
+ public override string ContainerId => "DisposalTagger";
+
[ViewVariables(VVAccess.ReadWrite)]
private string _tag = "";
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs
index e0d2908929..984be69018 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs
@@ -1,4 +1,4 @@
-using Content.Server.Disposal.Unit.Components;
+using Content.Server.Disposal.Unit.Components;
namespace Content.Server.Disposal.Tube.Components
{
@@ -9,6 +9,8 @@ namespace Content.Server.Disposal.Tube.Components
[ComponentReference(typeof(DisposalTubeComponent))]
public class DisposalTransitComponent : DisposalTubeComponent
{
+ public override string ContainerId => "DisposalTransit";
+
protected override Direction[] ConnectableDirections()
{
var rotation = IoCManager.Resolve().GetComponent(Owner).LocalRotation;
diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
index 3692937930..5547157676 100644
--- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
+++ b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
@@ -12,7 +12,7 @@ namespace Content.Server.Disposal.Tube.Components
{
public abstract class DisposalTubeComponent : Component, IDisposalTubeComponent
{
- public const string ContainerId = "disposal-tube";
+ public virtual string ContainerId => "DisposalTube";
[Dependency] private readonly IEntityManager _entMan = default!;
diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
index 493c705d2b..2840ddf3c3 100644
--- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
+++ b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
@@ -6,7 +6,7 @@ namespace Content.Shared.Disposal.Components
[NetworkedComponent]
public abstract class SharedDisposalUnitComponent : Component
{
- public const string ContainerId = "disposal-unit";
+ public const string ContainerId = "DisposalUnit";
// TODO: Could maybe turn the contact off instead far more cheaply as farseer (though not box2d) had support for it?
// Need to suss it out.
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
index 67bbf0d630..6578ee8f7b 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
@@ -52,8 +52,7 @@
- type: CollideOnAnchor
- type: ContainerContainer
containers:
- disposal-tube: !type:Container
- ents: []
+ DisposalTube: !type:Container
- type: entity
id: DisposalHolder
@@ -61,6 +60,9 @@
name: disposal holder
components:
- type: DisposalHolder
+ - type: ContainerContainer
+ containers:
+ DisposalHolder: !type:Container
# Pipes
@@ -93,6 +95,9 @@
- map: [ "pipe" ]
state: conpipe-s
- type: DisposalTransit
+ - type: ContainerContainer
+ containers:
+ DisposalTransit: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -116,6 +121,9 @@
- map: [ "pipe" ]
state: conpipe-tagger
- type: DisposalTagger
+ - type: ContainerContainer
+ containers:
+ DisposalTagger: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -145,6 +153,9 @@
- map: [ "pipe" ]
state: conpipe-t
- type: DisposalEntry
+ - type: ContainerContainer
+ containers:
+ DisposalEntry: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -179,6 +190,9 @@
- 0
- -90
- 180
+ - type: ContainerContainer
+ containers:
+ DisposalRouter: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -221,6 +235,9 @@
- 0
- 90
- 180
+ - type: ContainerContainer
+ containers:
+ DisposalRouter: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -255,6 +272,9 @@
- 0
- -90
- 180
+ - type: ContainerContainer
+ containers:
+ DisposalJunction: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -291,6 +311,9 @@
- 0
- 90
- 180
+ - type: ContainerContainer
+ containers:
+ DisposalJunction: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -324,6 +347,9 @@
- 0
- 90
- -90
+ - type: ContainerContainer
+ containers:
+ DisposalJunction: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
@@ -354,6 +380,9 @@
- map: [ "pipe" ]
state: conpipe-c
- type: DisposalBend
+ - type: ContainerContainer
+ containers:
+ DisposalBend: !type:Container
- type: GenericVisualizer
visuals:
enum.DisposalTubeVisuals.VisualState:
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
index 744cd5a4a2..b00409dd87 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
@@ -72,8 +72,7 @@
type: DisposalUnitBoundUserInterface
- type: ContainerContainer
containers:
- disposal-unit: !type:Container
- ents: []
+ DisposalUnit: !type:Container
- type: entity
id: DisposalUnit
From 9fd25937fbfaae5be914a243466efbd871d46ff1 Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Wed, 17 Aug 2022 15:44:43 +1200
Subject: [PATCH 013/109] Fix empty guns (#10650)
---
.../Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
index 5bc7cf9d5d..148afd9eb1 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
@@ -127,8 +127,13 @@ public abstract partial class SharedGunSystem
private void OnBallisticMapInit(EntityUid uid, BallisticAmmoProviderComponent component, MapInitEvent args)
{
+ // TODO this should be part of the prototype, not set on map init.
+ // Alternatively, just track spawned count, instead of unspawned count.
if (component.FillProto != null)
- component.UnspawnedCount -= Math.Min(component.UnspawnedCount, component.Container.ContainedEntities.Count);
+ {
+ component.UnspawnedCount = Math.Max(0, component.Capacity - component.Container.ContainedEntities.Count);
+ Dirty(component);
+ }
}
protected int GetBallisticShots(BallisticAmmoProviderComponent component)
From 0221f8ab6d8c165946240a6fd33b467058ce95ed Mon Sep 17 00:00:00 2001
From: PJBot
Date: Tue, 16 Aug 2022 23:45:46 -0400
Subject: [PATCH 014/109] Automatic changelog update
---
Resources/Changelog/Changelog.yml | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml
index 04f8b6e077..a9749cc6dd 100644
--- a/Resources/Changelog/Changelog.yml
+++ b/Resources/Changelog/Changelog.yml
@@ -1,10 +1,4 @@
Entries:
-- author: Rane
- changes:
- - {message: Added mute toxin., type: Add}
- - {message: Silencer now mutes you., type: Add}
- id: 1666
- time: '2022-06-06T08:35:25.0000000+00:00'
- author: Veritius
changes:
- {message: You can now opt in to being a nukie., type: Fix}
@@ -2933,3 +2927,8 @@ Entries:
- {message: Fixed typos on a few descriptions., type: Tweak}
id: 2165
time: '2022-08-17T00:56:54.0000000+00:00'
+- author: ElectroJr
+ changes:
+ - {message: Fixed guns spawning w/o ammo., type: Fix}
+ id: 2166
+ time: '2022-08-17T03:44:43.0000000+00:00'
From 04b943f2aad9c1df967e5177c8c78a6571ac9dbb Mon Sep 17 00:00:00 2001
From: OctoRocket <88291550+OctoRocket@users.noreply.github.com>
Date: Tue, 16 Aug 2022 22:49:48 -0500
Subject: [PATCH 015/109] Fixed matchbox issue (#10648)
---
Resources/Prototypes/Entities/Objects/Tools/matches.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Resources/Prototypes/Entities/Objects/Tools/matches.yml b/Resources/Prototypes/Entities/Objects/Tools/matches.yml
index 820c63d770..c5888f8f3b 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/matches.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/matches.yml
@@ -6,7 +6,7 @@
- type: Storage
capacity: 10
- type: Item
- size: 6
+ size: 10
- type: entity
name: match stick
From 1b50928d5071ce257af5b4e815903e58d670245c Mon Sep 17 00:00:00 2001
From: Flipp Syder <76629141+vulppine@users.noreply.github.com>
Date: Tue, 16 Aug 2022 21:03:23 -0700
Subject: [PATCH 016/109] Station records patches (#10636)
---
.../UI/IdCardConsoleBoundUserInterface.cs | 5 +-
.../Access/UI/IdCardConsoleWindow.xaml.cs | 19 ++++++-
.../CrewManifest/CrewManifestUi.xaml.cs | 10 ++--
.../Components/IdCardConsoleComponent.cs | 52 +++++++++++++++----
.../SharedIdCardConsoleComponent.cs | 7 ++-
.../components/id-card-console-component.ftl | 2 +-
6 files changed, 72 insertions(+), 23 deletions(-)
diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
index c5d0227e40..1591193eea 100644
--- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
+++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs
@@ -59,7 +59,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState);
}
- public void SubmitData(string newFullName, string newJobTitle, List newAccessList)
+ public void SubmitData(string newFullName, string newJobTitle, List newAccessList, string newJobPrototype)
{
if (newFullName.Length > MaxFullNameLength)
newFullName = newFullName[..MaxFullNameLength];
@@ -70,7 +70,8 @@ namespace Content.Client.Access.UI
SendMessage(new WriteToTargetIdMessage(
newFullName,
newJobTitle,
- newAccessList));
+ newAccessList,
+ newJobPrototype));
}
}
}
diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
index 359ad44d52..e4594856f9 100644
--- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
+++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
@@ -26,8 +26,10 @@ namespace Content.Client.Access.UI
private string? _lastFullName;
private string? _lastJobTitle;
+ private string? _lastJobProto;
- public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager, List accessLevels)
+ public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
+ List accessLevels)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -101,6 +103,7 @@ namespace Content.Client.Access.UI
}
JobTitleLineEdit.Text = Loc.GetString(job.Name);
+ args.Button.SelectId(args.Id);
ClearAllAccess();
@@ -181,17 +184,29 @@ namespace Content.Client.Access.UI
}
}
+ var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
+ if (jobIndex >= 0)
+ {
+ JobPresetOptionButton.SelectId(jobIndex);
+ }
+
_lastFullName = state.TargetIdFullName;
_lastJobTitle = state.TargetIdJobTitle;
+ _lastJobProto = state.TargetIdJobPrototype;
}
private void SubmitData()
{
+ // Don't send this if it isn't dirty.
+ var jobProtoDirty = _lastJobProto != null &&
+ _jobPrototypeIds[JobPresetOptionButton.SelectedId] != _lastJobProto;
+
_owner.SubmitData(
FullNameLineEdit.Text,
JobTitleLineEdit.Text,
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
- _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
+ _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
+ jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty);
}
}
}
diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs
index d36fa3a2c0..5c7d371a09 100644
--- a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs
+++ b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs
@@ -131,11 +131,11 @@ public sealed partial class CrewManifestUi : DefaultWindow
foreach (var entry in entries)
{
- var name = new Label()
+ var name = new RichTextLabel()
{
HorizontalExpand = true,
- Text = entry.Name
};
+ name.SetMessage(entry.Name);
var titleContainer = new BoxContainer()
{
@@ -143,10 +143,8 @@ public sealed partial class CrewManifestUi : DefaultWindow
HorizontalExpand = true
};
- var title = new Label()
- {
- Text = Loc.GetString(entry.JobTitle)
- };
+ var title = new RichTextLabel();
+ title.SetMessage(Loc.GetString(entry.JobTitle));
if (rsi != null)
diff --git a/Content.Server/Access/Components/IdCardConsoleComponent.cs b/Content.Server/Access/Components/IdCardConsoleComponent.cs
index 1dd27f87d8..da00c06aec 100644
--- a/Content.Server/Access/Components/IdCardConsoleComponent.cs
+++ b/Content.Server/Access/Components/IdCardConsoleComponent.cs
@@ -8,7 +8,9 @@ using Content.Shared.Access.Systems;
using Content.Shared.StationRecords;
using Content.Server.Administration.Logs;
using Content.Shared.Database;
+using Content.Shared.Roles;
using Robust.Server.GameObjects;
+using Robust.Shared.Prototypes;
namespace Content.Server.Access.Components
{
@@ -18,9 +20,13 @@ namespace Content.Server.Access.Components
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
+ private StationRecordsSystem? _recordSystem;
+ private StationSystem? _stationSystem;
+
protected override void Initialize()
{
base.Initialize();
@@ -28,6 +34,9 @@ namespace Content.Server.Access.Components
Owner.EnsureComponentWarn();
Owner.EnsureComponentWarn();
+ _stationSystem = _entities.EntitySysManager.GetEntitySystem();
+ _recordSystem = _entities.EntitySysManager.GetEntitySystem();
+
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
@@ -45,7 +54,7 @@ namespace Content.Server.Access.Components
switch (obj.Message)
{
case WriteToTargetIdMessage msg:
- TryWriteToTargetId(msg.FullName, msg.JobTitle, msg.AccessList, player);
+ TryWriteToTargetId(msg.FullName, msg.JobTitle, msg.AccessList, msg.JobPrototype, player);
UpdateUserInterface();
break;
}
@@ -62,7 +71,7 @@ namespace Content.Server.Access.Components
}
var privilegedIdEntity = PrivilegedIdSlot.Item;
- var accessSystem = EntitySystem.Get();
+ var accessSystem = _entities.EntitySysManager.GetEntitySystem();
return privilegedIdEntity != null && accessSystem.IsAllowed(privilegedIdEntity.Value, reader);
}
@@ -70,12 +79,12 @@ namespace Content.Server.Access.Components
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
/// Writes data passed from the UI into the ID stored in , if present.
///
- private void TryWriteToTargetId(string newFullName, string newJobTitle, List newAccessList, EntityUid player)
+ private void TryWriteToTargetId(string newFullName, string newJobTitle, List newAccessList, string newJobProto, EntityUid player)
{
if (TargetIdSlot.Item is not {Valid: true} targetIdEntity || !PrivilegedIdIsAuthorized())
return;
- var cardSystem = EntitySystem.Get();
+ var cardSystem = _entities.EntitySysManager.GetEntitySystem();
cardSystem.TryChangeFullName(targetIdEntity, newFullName, player: player);
cardSystem.TryChangeJobTitle(targetIdEntity, newJobTitle, player: player);
@@ -85,7 +94,7 @@ namespace Content.Server.Access.Components
return;
}
- var accessSystem = EntitySystem.Get();
+ var accessSystem = _entities.EntitySysManager.GetEntitySystem();
accessSystem.TrySetTags(targetIdEntity, newAccessList);
/*TODO: ECS IdCardConsoleComponent and then log on card ejection, together with the save.
@@ -93,17 +102,17 @@ namespace Content.Server.Access.Components
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{_entities.ToPrettyString(player):player} has modified {_entities.ToPrettyString(targetIdEntity):entity} with the following accesses: [{string.Join(", ", newAccessList)}]");
- UpdateStationRecord(targetIdEntity, newFullName, newJobTitle);
+ UpdateStationRecord(targetIdEntity, newFullName, newJobTitle, newJobProto);
}
- private void UpdateStationRecord(EntityUid idCard, string newFullName, string newJobTitle)
+ private void UpdateStationRecord(EntityUid idCard, string newFullName, string newJobTitle, string newJobProto)
{
- var station = EntitySystem.Get().GetOwningStation(Owner);
- var recordSystem = EntitySystem.Get();
+ var station = _stationSystem?.GetOwningStation(Owner);
if (station == null
+ || _recordSystem == null
|| !_entities.TryGetComponent(idCard, out StationRecordKeyStorageComponent? keyStorage)
|| keyStorage.Key == null
- || !recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value, out GeneralStationRecord? record))
+ || !_recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value, out GeneralStationRecord? record))
{
return;
}
@@ -111,7 +120,13 @@ namespace Content.Server.Access.Components
record.Name = newFullName;
record.JobTitle = newJobTitle;
- recordSystem.Synchronize(station.Value);
+ if (_prototypeManager.TryIndex(newJobProto, out JobPrototype? job))
+ {
+ record.JobPrototype = newJobProto;
+ record.JobIcon = job.Icon;
+ }
+
+ _recordSystem.Synchronize(station.Value);
}
public void UpdateUserInterface()
@@ -137,6 +152,7 @@ namespace Content.Server.Access.Components
null,
null,
privilegedIdName,
+ string.Empty,
string.Empty);
}
else
@@ -146,6 +162,19 @@ namespace Content.Server.Access.Components
var name = string.Empty;
if (PrivilegedIdSlot.Item is {Valid: true} item)
name = _entities.GetComponent(item).EntityName;
+
+ var station = _stationSystem?.GetOwningStation(Owner);
+ var jobProto = string.Empty;
+ if (_recordSystem != null
+ && station != null
+ && _entities.TryGetComponent(targetIdEntity, out StationRecordKeyStorageComponent? keyStorage)
+ && keyStorage.Key != null
+ && _recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value,
+ out GeneralStationRecord? record))
+ {
+ jobProto = record.JobPrototype;
+ }
+
newState = new IdCardConsoleBoundUserInterfaceState(
PrivilegedIdSlot.HasItem,
PrivilegedIdIsAuthorized(),
@@ -153,6 +182,7 @@ namespace Content.Server.Access.Components
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(),
+ jobProto,
name,
_entities.GetComponent(targetIdEntity).EntityName);
}
diff --git a/Content.Shared/Access/Components/SharedIdCardConsoleComponent.cs b/Content.Shared/Access/Components/SharedIdCardConsoleComponent.cs
index 49406d059a..75e7fb7607 100644
--- a/Content.Shared/Access/Components/SharedIdCardConsoleComponent.cs
+++ b/Content.Shared/Access/Components/SharedIdCardConsoleComponent.cs
@@ -26,12 +26,14 @@ namespace Content.Shared.Access.Components
public readonly string FullName;
public readonly string JobTitle;
public readonly List AccessList;
+ public readonly string JobPrototype;
- public WriteToTargetIdMessage(string fullName, string jobTitle, List accessList)
+ public WriteToTargetIdMessage(string fullName, string jobTitle, List accessList, string jobPrototype)
{
FullName = fullName;
JobTitle = jobTitle;
AccessList = accessList;
+ JobPrototype = jobPrototype;
}
}
@@ -82,6 +84,7 @@ namespace Content.Shared.Access.Components
public readonly string? TargetIdFullName;
public readonly string? TargetIdJobTitle;
public readonly string[]? TargetIdAccessList;
+ public readonly string TargetIdJobPrototype;
public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized,
@@ -89,6 +92,7 @@ namespace Content.Shared.Access.Components
string? targetIdFullName,
string? targetIdJobTitle,
string[]? targetIdAccessList,
+ string targetIdJobPrototype,
string privilegedIdName,
string targetIdName)
{
@@ -98,6 +102,7 @@ namespace Content.Shared.Access.Components
TargetIdFullName = targetIdFullName;
TargetIdJobTitle = targetIdJobTitle;
TargetIdAccessList = targetIdAccessList;
+ TargetIdJobPrototype = targetIdJobPrototype;
PrivilegedIdName = privilegedIdName;
TargetIdName = targetIdName;
}
diff --git a/Resources/Locale/en-US/access/components/id-card-console-component.ftl b/Resources/Locale/en-US/access/components/id-card-console-component.ftl
index 79e9aedb78..be5d3f0bc3 100644
--- a/Resources/Locale/en-US/access/components/id-card-console-component.ftl
+++ b/Resources/Locale/en-US/access/components/id-card-console-component.ftl
@@ -5,7 +5,7 @@ id-card-console-window-save-button = Save
id-card-console-window-job-title-label = Job title:
id-card-console-window-eject-button = Eject
id-card-console-window-insert-button = Insert
-id-card-console-window-job-selection-label = Job presets:
+id-card-console-window-job-selection-label = Job presets (sets department and job icon):
access-id-card-console-component-no-hands-error = You have no hands.
id-card-console-privileged-id = Privileged ID
From 2152914acc666d8dd7b1f1a8864d5738fa5ff7c4 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Wed, 17 Aug 2022 00:34:25 -0400
Subject: [PATCH 017/109] Generalized Store System (#10201)
---
.../Revenant/Ui/RevenantBoundUserInterface.cs | 2 -
.../Store/Ui/StoreBoundUserInterface.cs | 79 ++
.../Store/Ui/StoreListingControl.xaml | 21 +
.../Store/Ui/StoreListingControl.xaml.cs | 24 +
Content.Client/Store/Ui/StoreMenu.xaml | 54 ++
Content.Client/Store/Ui/StoreMenu.xaml.cs | 222 +++++
.../Store/Ui/StoreWithdrawWindow.xaml | 16 +
.../Store/Ui/StoreWithdrawWindow.xaml.cs | 102 +++
.../Uplink/UplinkBoundUserInterface.cs | 65 --
.../Traitor/Uplink/UplinkListingControl.xaml | 22 -
.../Uplink/UplinkListingControl.xaml.cs | 28 -
Content.Client/Traitor/Uplink/UplinkMenu.xaml | 53 --
.../Traitor/Uplink/UplinkMenu.xaml.cs | 163 ----
.../Traitor/Uplink/UplinkWithdrawWindow.xaml | 22 -
.../Uplink/UplinkWithdrawWindow.xaml.cs | 34 -
.../GameTicking/Rules/NukeopsRuleSystem.cs | 3 +
.../GameTicking/Rules/SuspicionRuleSystem.cs | 13 +-
.../Rules/TraitorDeathMatchRuleSystem.cs | 34 +-
.../GameTicking/Rules/TraitorRuleSystem.cs | 18 +-
Content.Server/PDA/PDASystem.cs | 36 +-
.../Store/Components/CurrencyComponent.cs | 22 +
.../Store/Components/StoreComponent.cs | 91 +++
.../Store/Conditions/BuyerAntagCondition.cs | 63 ++
.../Store/Conditions/BuyerJobCondition.cs | 63 ++
.../Conditions/BuyerWhitelistCondition.cs | 41 +
.../ListingLimitedStockCondition.cs | 20 +
.../Conditions/StoreWhitelistCondition.cs | 44 +
.../Store/Systems/StoreSystem.Listings.cs | 127 +++
.../Store/Systems/StoreSystem.Ui.cs | 226 ++++++
Content.Server/Store/Systems/StoreSystem.cs | 154 ++++
.../Uplink/Account/UplinkAccountEvents.cs | 30 -
.../Uplink/Account/UplinkAccountsSystem.cs | 108 ---
.../Uplink/Commands/AddUplinkCommand.cs | 14 +-
.../SurplusBundle/SurplusBundleComponent.cs | 10 +
.../SurplusBundle/SurplusBundleSystem.cs | 41 +-
.../Telecrystal/TelecrystalComponent.cs | 7 -
.../Uplink/Telecrystal/TelecrystalSystem.cs | 52 --
.../Traitor/Uplink/UplinkComponent.cs | 38 -
Content.Server/Traitor/Uplink/UplinkEvents.cs | 18 -
.../Traitor/Uplink/UplinkListingSytem.cs | 53 --
Content.Server/Traitor/Uplink/UplinkSystem.cs | 256 +-----
.../TraitorDeathMatchRedemptionSystem.cs | 51 +-
Content.Shared.Database/LogType.cs | 1 +
Content.Shared/PDA/UplinkCategory.cs | 17 -
.../PDA/UplinkStoreListingPrototype.cs | 40 -
Content.Shared/Store/CurrencyPrototype.cs | 43 +
Content.Shared/Store/ListingCondition.cs | 23 +
Content.Shared/Store/ListingPrototype.cs | 133 +++
.../Store/StoreCategoryPrototype.cs | 22 +
Content.Shared/Store/StorePresetPrototype.cs | 41 +
Content.Shared/Store/StoreUi.cs | 84 ++
.../Traitor/Uplink/UplinkAccount.cs | 14 -
.../Traitor/Uplink/UplinkAccountData.cs | 17 -
.../Traitor/Uplink/UplinkListingData.cs | 41 -
.../Traitor/Uplink/UplinkMessagesUI.cs | 35 -
.../Traitor/Uplink/UplinkNetworkEvents.cs | 14 -
.../Traitor/Uplink/UplinkUpdateState.cs | 17 -
.../Traitor/Uplink/UplinkVisuals.cs | 10 -
Resources/Locale/en-US/store/currency.ftl | 11 +
Resources/Locale/en-US/store/store.ftl | 4 +
Resources/Prototypes/Catalog/catalog.yml | 44 +
.../Prototypes/Catalog/uplink_catalog.yml | 761 +++++++++++-------
.../Entities/Objects/Devices/pda.yml | 4 +-
.../Entities/Objects/Specific/syndicate.yml | 54 +-
Resources/Prototypes/Store/categories.yml | 60 ++
Resources/Prototypes/Store/currency.yml | 12 +
Resources/Prototypes/Store/presets.yml | 16 +
Resources/Prototypes/tags.yml | 3 +
68 files changed, 2493 insertions(+), 1568 deletions(-)
create mode 100644 Content.Client/Store/Ui/StoreBoundUserInterface.cs
create mode 100644 Content.Client/Store/Ui/StoreListingControl.xaml
create mode 100644 Content.Client/Store/Ui/StoreListingControl.xaml.cs
create mode 100644 Content.Client/Store/Ui/StoreMenu.xaml
create mode 100644 Content.Client/Store/Ui/StoreMenu.xaml.cs
create mode 100644 Content.Client/Store/Ui/StoreWithdrawWindow.xaml
create mode 100644 Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs
delete mode 100644 Content.Client/Traitor/Uplink/UplinkBoundUserInterface.cs
delete mode 100644 Content.Client/Traitor/Uplink/UplinkListingControl.xaml
delete mode 100644 Content.Client/Traitor/Uplink/UplinkListingControl.xaml.cs
delete mode 100644 Content.Client/Traitor/Uplink/UplinkMenu.xaml
delete mode 100644 Content.Client/Traitor/Uplink/UplinkMenu.xaml.cs
delete mode 100644 Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml
delete mode 100644 Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml.cs
create mode 100644 Content.Server/Store/Components/CurrencyComponent.cs
create mode 100644 Content.Server/Store/Components/StoreComponent.cs
create mode 100644 Content.Server/Store/Conditions/BuyerAntagCondition.cs
create mode 100644 Content.Server/Store/Conditions/BuyerJobCondition.cs
create mode 100644 Content.Server/Store/Conditions/BuyerWhitelistCondition.cs
create mode 100644 Content.Server/Store/Conditions/ListingLimitedStockCondition.cs
create mode 100644 Content.Server/Store/Conditions/StoreWhitelistCondition.cs
create mode 100644 Content.Server/Store/Systems/StoreSystem.Listings.cs
create mode 100644 Content.Server/Store/Systems/StoreSystem.Ui.cs
create mode 100644 Content.Server/Store/Systems/StoreSystem.cs
delete mode 100644 Content.Server/Traitor/Uplink/Account/UplinkAccountEvents.cs
delete mode 100644 Content.Server/Traitor/Uplink/Account/UplinkAccountsSystem.cs
delete mode 100644 Content.Server/Traitor/Uplink/Telecrystal/TelecrystalComponent.cs
delete mode 100644 Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs
delete mode 100644 Content.Server/Traitor/Uplink/UplinkComponent.cs
delete mode 100644 Content.Server/Traitor/Uplink/UplinkEvents.cs
delete mode 100644 Content.Server/Traitor/Uplink/UplinkListingSytem.cs
delete mode 100644 Content.Shared/PDA/UplinkCategory.cs
delete mode 100644 Content.Shared/PDA/UplinkStoreListingPrototype.cs
create mode 100644 Content.Shared/Store/CurrencyPrototype.cs
create mode 100644 Content.Shared/Store/ListingCondition.cs
create mode 100644 Content.Shared/Store/ListingPrototype.cs
create mode 100644 Content.Shared/Store/StoreCategoryPrototype.cs
create mode 100644 Content.Shared/Store/StorePresetPrototype.cs
create mode 100644 Content.Shared/Store/StoreUi.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkAccount.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkAccountData.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkListingData.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkMessagesUI.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkNetworkEvents.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkUpdateState.cs
delete mode 100644 Content.Shared/Traitor/Uplink/UplinkVisuals.cs
create mode 100644 Resources/Locale/en-US/store/currency.ftl
create mode 100644 Resources/Locale/en-US/store/store.ftl
create mode 100644 Resources/Prototypes/Catalog/catalog.yml
create mode 100644 Resources/Prototypes/Store/categories.yml
create mode 100644 Resources/Prototypes/Store/currency.yml
create mode 100644 Resources/Prototypes/Store/presets.yml
diff --git a/Content.Client/Revenant/Ui/RevenantBoundUserInterface.cs b/Content.Client/Revenant/Ui/RevenantBoundUserInterface.cs
index 9fc45e3aff..17f7d88875 100644
--- a/Content.Client/Revenant/Ui/RevenantBoundUserInterface.cs
+++ b/Content.Client/Revenant/Ui/RevenantBoundUserInterface.cs
@@ -1,6 +1,4 @@
-using Content.Client.Traitor.Uplink;
using Content.Shared.Revenant;
-using Content.Shared.Traitor.Uplink;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
new file mode 100644
index 0000000000..0122decdc2
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
@@ -0,0 +1,79 @@
+using Content.Shared.Store;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+using System.Linq;
+
+namespace Content.Client.Store.Ui;
+
+[UsedImplicitly]
+public sealed class StoreBoundUserInterface : BoundUserInterface
+{
+ private StoreMenu? _menu;
+
+ private string _windowName = Loc.GetString("store-ui-default-title");
+
+ public StoreBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ _menu = new StoreMenu(_windowName);
+
+ _menu.OpenCentered();
+ _menu.OnClose += Close;
+
+ _menu.OnListingButtonPressed += (_, listing) =>
+ {
+ if (_menu.CurrentBuyer != null)
+ SendMessage(new StoreBuyListingMessage(_menu.CurrentBuyer.Value, listing));
+ };
+
+ _menu.OnCategoryButtonPressed += (_, category) =>
+ {
+ _menu.CurrentCategory = category;
+ if (_menu.CurrentBuyer != null)
+ SendMessage(new StoreRequestUpdateInterfaceMessage(_menu.CurrentBuyer.Value));
+ };
+
+ _menu.OnWithdrawAttempt += (_, type, amount) =>
+ {
+ if (_menu.CurrentBuyer != null)
+ SendMessage(new StoreRequestWithdrawMessage(_menu.CurrentBuyer.Value, type, amount));
+ };
+ }
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (_menu == null)
+ return;
+
+ switch (state)
+ {
+ case StoreUpdateState msg:
+ if (msg.Buyer != null)
+ _menu.CurrentBuyer = msg.Buyer;
+ _menu.UpdateBalance(msg.Balance);
+ _menu.PopulateStoreCategoryButtons(msg.Listings);
+ _menu.UpdateListing(msg.Listings.ToList());
+ break;
+ case StoreInitializeState msg:
+ _windowName = msg.Name;
+ if (_menu != null && _menu.Window != null)
+ _menu.Window.Title = msg.Name;
+ break;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ _menu?.Close();
+ _menu?.Dispose();
+ }
+}
diff --git a/Content.Client/Store/Ui/StoreListingControl.xaml b/Content.Client/Store/Ui/StoreListingControl.xaml
new file mode 100644
index 0000000000..aefeec17cc
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreListingControl.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Store/Ui/StoreListingControl.xaml.cs b/Content.Client/Store/Ui/StoreListingControl.xaml.cs
new file mode 100644
index 0000000000..073d627439
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreListingControl.xaml.cs
@@ -0,0 +1,24 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Store.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class StoreListingControl : Control
+{
+ public StoreListingControl(string itemName, string itemDescription,
+ string price, bool canBuy, Texture? texture = null)
+ {
+ RobustXamlLoader.Load(this);
+
+ StoreItemName.Text = itemName;
+ StoreItemDescription.SetMessage(itemDescription);
+
+ StoreItemBuyButton.Text = price;
+ StoreItemBuyButton.Disabled = !canBuy;
+
+ StoreItemTexture.Texture = texture;
+ }
+}
diff --git a/Content.Client/Store/Ui/StoreMenu.xaml b/Content.Client/Store/Ui/StoreMenu.xaml
new file mode 100644
index 0000000000..fbbf6c1343
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreMenu.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs
new file mode 100644
index 0000000000..b916b8e890
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs
@@ -0,0 +1,222 @@
+using Content.Client.Message;
+using Content.Shared.Store;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Robust.Client.Graphics;
+using Content.Shared.Actions.ActionTypes;
+using System.Linq;
+using Content.Shared.FixedPoint;
+
+namespace Content.Client.Store.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class StoreMenu : DefaultWindow
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private StoreWithdrawWindow? _withdrawWindow;
+
+ public event Action? OnListingButtonPressed;
+ public event Action? OnCategoryButtonPressed;
+ public event Action? OnWithdrawAttempt;
+
+ public EntityUid? CurrentBuyer;
+ public Dictionary Balance = new();
+ public string CurrentCategory = string.Empty;
+
+ public StoreMenu(string name)
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
+ if (Window != null)
+ Window.Title = name;
+ }
+
+ public void UpdateBalance(Dictionary balance)
+ {
+ Balance = balance;
+
+ var currency = new Dictionary<(string, FixedPoint2), CurrencyPrototype>();
+ foreach (var type in balance)
+ {
+ currency.Add((type.Key, type.Value), _prototypeManager.Index(type.Key));
+ }
+
+ var balanceStr = string.Empty;
+ foreach (var type in currency)
+ {
+ balanceStr += $"{Loc.GetString(type.Value.BalanceDisplay, ("amount", type.Key.Item2))}\n";
+ }
+
+ BalanceInfo.SetMarkup(balanceStr.TrimEnd());
+
+ var disabled = true;
+ foreach (var type in currency)
+ {
+ if (type.Value.CanWithdraw && type.Value.EntityId != null && type.Key.Item2 > 0)
+ disabled = false;
+ }
+
+ WithdrawButton.Disabled = disabled;
+ }
+
+ public void UpdateListing(List listings)
+ {
+ var sorted = listings.OrderBy(l => l.Priority).ThenBy(l => l.Cost.Values.Sum());
+
+ // should probably chunk these out instead. to-do if this clogs the internet tubes.
+ // maybe read clients prototypes instead?
+ ClearListings();
+ foreach (var item in sorted)
+ {
+ AddListingGui(item);
+ }
+ }
+
+ private void OnWithdrawButtonDown(BaseButton.ButtonEventArgs args)
+ {
+ // check if window is already open
+ if (_withdrawWindow != null && _withdrawWindow.IsOpen)
+ {
+ _withdrawWindow.MoveToFront();
+ return;
+ }
+
+ // open a new one
+ _withdrawWindow = new StoreWithdrawWindow();
+ _withdrawWindow.OpenCentered();
+
+ _withdrawWindow.CreateCurrencyButtons(Balance);
+ _withdrawWindow.OnWithdrawAttempt += OnWithdrawAttempt;
+ }
+
+ private void AddListingGui(ListingData listing)
+ {
+ if (!listing.Categories.Contains(CurrentCategory))
+ return;
+
+ string listingName = new (listing.Name);
+ string listingDesc = new (listing.Description);
+ var listingPrice = listing.Cost;
+ var canBuy = CanBuyListing(Balance, listingPrice);
+
+ var spriteSys = _entityManager.EntitySysManager.GetEntitySystem();
+
+ Texture? texture = null;
+ if (listing.Icon != null)
+ texture = spriteSys.Frame0(listing.Icon);
+
+ if (listing.ProductEntity != null)
+ {
+ if (texture == null)
+ texture = spriteSys.GetPrototypeIcon(listing.ProductEntity).Default;
+
+ var proto = _prototypeManager.Index(listing.ProductEntity);
+ if (listingName == string.Empty)
+ listingName = proto.Name;
+ if (listingDesc == string.Empty)
+ listingDesc = proto.Description;
+ }
+ else if (listing.ProductAction != null)
+ {
+ var action = _prototypeManager.Index(listing.ProductAction);
+ if (action.Icon != null)
+ texture = spriteSys.Frame0(action.Icon);
+ }
+
+ var newListing = new StoreListingControl(listingName, listingDesc, GetListingPriceString(listing), canBuy, texture);
+ newListing.StoreItemBuyButton.OnButtonDown += args
+ => OnListingButtonPressed?.Invoke(args, listing);
+
+ StoreListingsContainer.AddChild(newListing);
+ }
+
+ public bool CanBuyListing(Dictionary currency, Dictionary price)
+ {
+ foreach (var type in price)
+ {
+ if (!currency.ContainsKey(type.Key))
+ return false;
+
+ if (currency[type.Key] < type.Value)
+ return false;
+ }
+ return true;
+ }
+
+ public string GetListingPriceString(ListingData listing)
+ {
+ var text = string.Empty;
+
+ foreach (var type in listing.Cost)
+ {
+ var currency = _prototypeManager.Index(type.Key);
+ text += $"{Loc.GetString(currency.PriceDisplay, ("amount", type.Value))}\n";
+ }
+
+ if (listing.Cost.Count < 1)
+ text = Loc.GetString("store-currency-free");
+
+ return text.TrimEnd();
+ }
+
+ private void ClearListings()
+ {
+ StoreListingsContainer.Children.Clear();
+ }
+
+ public void PopulateStoreCategoryButtons(HashSet listings)
+ {
+ var allCategories = new List();
+ foreach (var listing in listings)
+ {
+ foreach (var cat in listing.Categories)
+ {
+ var proto = _prototypeManager.Index(cat);
+ if (!allCategories.Contains(proto))
+ allCategories.Add(proto);
+ }
+ }
+
+ allCategories = allCategories.OrderBy(c => c.Priority).ToList();
+
+ if (CurrentCategory == string.Empty && allCategories.Count > 0)
+ CurrentCategory = allCategories.First().ID;
+
+ if (allCategories.Count <= 1)
+ return;
+
+ CategoryListContainer.Children.Clear();
+
+ foreach (var proto in allCategories)
+ {
+ var catButton = new StoreCategoryButton
+ {
+ Text = Loc.GetString(proto.Name),
+ Id = proto.ID
+ };
+
+ catButton.OnPressed += args => OnCategoryButtonPressed?.Invoke(args, catButton.Id);
+ CategoryListContainer.AddChild(catButton);
+ }
+ }
+
+ public override void Close()
+ {
+ base.Close();
+ CurrentBuyer = null;
+ _withdrawWindow?.Close();
+ }
+
+ private sealed class StoreCategoryButton : Button
+ {
+ public string? Id;
+ }
+}
diff --git a/Content.Client/Store/Ui/StoreWithdrawWindow.xaml b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml
new file mode 100644
index 0000000000..ac68c1e237
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs
new file mode 100644
index 0000000000..968e3ed610
--- /dev/null
+++ b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs
@@ -0,0 +1,102 @@
+using System.Linq;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Localization;
+using Content.Shared.FixedPoint;
+using Content.Shared.Store;
+using Robust.Client.UserInterface;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Robust.Client.Graphics;
+using Content.Shared.Actions.ActionTypes;
+using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Store.Ui;
+
+///
+/// Window to select amount TC to withdraw from Uplink account
+/// Used as sub-window in Uplink UI
+///
+[GenerateTypedNameReferences]
+public sealed partial class StoreWithdrawWindow : DefaultWindow
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private Dictionary _validCurrencies = new();
+ private HashSet _buttons = new();
+ public event Action? OnWithdrawAttempt;
+
+ public StoreWithdrawWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ }
+
+ public void CreateCurrencyButtons(Dictionary balance)
+ {
+ _validCurrencies.Clear();
+ foreach (var currency in balance)
+ {
+ if (!_prototypeManager.TryIndex(currency.Key, out var proto))
+ continue;
+
+ _validCurrencies.Add(currency.Value, proto);
+ }
+
+ //this shouldn't ever happen but w/e
+ if (_validCurrencies.Count < 1)
+ return;
+
+ ButtonContainer.Children.Clear();
+ _buttons.Clear();
+ foreach (var currency in _validCurrencies)
+ {
+ Logger.Debug((currency.Value.PriceDisplay));
+ var button = new CurrencyWithdrawButton()
+ {
+ Id = currency.Value.ID,
+ Amount = currency.Key,
+ MinHeight = 20,
+ Text = Loc.GetString("store-withdraw-button-ui", ("currency",Loc.GetString(currency.Value.PriceDisplay))),
+ };
+ button.Disabled = false;
+ button.OnPressed += args =>
+ {
+ OnWithdrawAttempt?.Invoke(args, button.Id, WithdrawSlider.Value);
+ Close();
+ };
+
+ _buttons.Add(button);
+ ButtonContainer.AddChild(button);
+ }
+
+ var maxWithdrawAmount = _validCurrencies.Keys.Max().Int();
+
+ // setup withdraw slider
+ WithdrawSlider.MinValue = 1;
+ WithdrawSlider.MaxValue = maxWithdrawAmount;
+
+ WithdrawSlider.OnValueChanged += OnValueChanged;
+ OnValueChanged(WithdrawSlider.Value);
+ }
+
+ public void OnValueChanged(int i)
+ {
+ foreach (var button in _buttons)
+ {
+ button.Disabled = button.Amount < WithdrawSlider.Value;
+ }
+ }
+
+ private sealed class CurrencyWithdrawButton : Button
+ {
+ public string? Id;
+ public FixedPoint2 Amount = FixedPoint2.Zero;
+ }
+}
diff --git a/Content.Client/Traitor/Uplink/UplinkBoundUserInterface.cs b/Content.Client/Traitor/Uplink/UplinkBoundUserInterface.cs
deleted file mode 100644
index c7051756f7..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkBoundUserInterface.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Content.Shared.Traitor.Uplink;
-using JetBrains.Annotations;
-using Robust.Client.GameObjects;
-
-namespace Content.Client.Traitor.Uplink
-{
- [UsedImplicitly]
- public sealed class UplinkBoundUserInterface : BoundUserInterface
- {
- private UplinkMenu? _menu;
-
- public UplinkBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
- {
-
- }
-
- protected override void Open()
- {
- _menu = new UplinkMenu();
- _menu.OpenCentered();
- _menu.OnClose += Close;
-
- _menu.OnListingButtonPressed += (_, listing) =>
- {
- SendMessage(new UplinkBuyListingMessage(listing.ItemId));
- };
-
- _menu.OnCategoryButtonPressed += (_, category) =>
- {
- _menu.CurrentFilterCategory = category;
- SendMessage(new UplinkRequestUpdateInterfaceMessage());
- };
-
- _menu.OnWithdrawAttempt += (tc) =>
- {
- SendMessage(new UplinkTryWithdrawTC(tc));
- };
- }
- protected override void UpdateState(BoundUserInterfaceState state)
- {
- base.UpdateState(state);
-
- if (_menu == null)
- return;
-
- switch (state)
- {
- case UplinkUpdateState msg:
- _menu.UpdateAccount(msg.Account);
- _menu.UpdateListing(msg.Listings);
- break;
- }
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Close();
- _menu?.Dispose();
- }
- }
-}
diff --git a/Content.Client/Traitor/Uplink/UplinkListingControl.xaml b/Content.Client/Traitor/Uplink/UplinkListingControl.xaml
deleted file mode 100644
index d6d1283ced..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkListingControl.xaml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Traitor/Uplink/UplinkListingControl.xaml.cs b/Content.Client/Traitor/Uplink/UplinkListingControl.xaml.cs
deleted file mode 100644
index 0bb269e58a..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkListingControl.xaml.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Robust.Client.AutoGenerated;
-using Robust.Client.Graphics;
-using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Maths;
-
-namespace Content.Client.Traitor.Uplink
-{
- [GenerateTypedNameReferences]
- public sealed partial class UplinkListingControl : Control
- {
-
- public UplinkListingControl(string itemName, string itemDescription,
- int itemPrice, bool canBuy, Texture? texture = null)
- {
- RobustXamlLoader.Load(this);
-
- UplinkItemName.Text = itemName;
- UplinkItemDescription.SetMessage(itemDescription);
-
- UplinkItemBuyButton.Text = $"{itemPrice} TC";
- UplinkItemBuyButton.Disabled = !canBuy;
-
- UplinkItemTexture.Texture = texture;
- }
- }
-}
diff --git a/Content.Client/Traitor/Uplink/UplinkMenu.xaml b/Content.Client/Traitor/Uplink/UplinkMenu.xaml
deleted file mode 100644
index 9ea5e2869e..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkMenu.xaml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Traitor/Uplink/UplinkMenu.xaml.cs b/Content.Client/Traitor/Uplink/UplinkMenu.xaml.cs
deleted file mode 100644
index 14c8c072df..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkMenu.xaml.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using Content.Client.Message;
-using Content.Shared.PDA;
-using Content.Shared.Traitor.Uplink;
-using Robust.Client.AutoGenerated;
-using Robust.Client.GameObjects;
-using Robust.Client.ResourceManagement;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Client.Utility;
-using Robust.Shared.Prototypes;
-
-namespace Content.Client.Traitor.Uplink
-{
- [GenerateTypedNameReferences]
- public sealed partial class UplinkMenu : DefaultWindow
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IResourceCache _resourceCache = default!;
-
- private UplinkWithdrawWindow? _withdrawWindow;
-
- public event Action? OnListingButtonPressed;
- public event Action? OnCategoryButtonPressed;
- public event Action? OnWithdrawAttempt;
-
- private UplinkCategory _currentFilter;
- private UplinkAccountData? _loggedInUplinkAccount;
-
- public UplinkMenu()
- {
- RobustXamlLoader.Load(this);
- IoCManager.InjectDependencies(this);
-
- PopulateUplinkCategoryButtons();
- WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
- }
-
- public UplinkCategory CurrentFilterCategory
- {
- get => _currentFilter;
- set
- {
- if (value.GetType() != typeof(UplinkCategory))
- {
- return;
- }
-
- _currentFilter = value;
- }
- }
-
- public void UpdateAccount(UplinkAccountData account)
- {
- _loggedInUplinkAccount = account;
-
- // update balance label
- var balance = account.DataBalance;
- var weightedColor = balance switch
- {
- <= 0 => "gray",
- <= 5 => "green",
- <= 20 => "yellow",
- <= 50 => "purple",
- _ => "gray"
- };
- var balanceStr = Loc.GetString("uplink-bound-user-interface-tc-balance-popup",
- ("weightedColor", weightedColor),
- ("balance", balance));
- BalanceInfo.SetMarkup(balanceStr);
-
- // you can't withdraw if you don't have TC
- WithdrawButton.Disabled = balance <= 0;
- }
-
- public void UpdateListing(UplinkListingData[] listings)
- {
- // should probably chunk these out instead. to-do if this clogs the internet tubes.
- // maybe read clients prototypes instead?
- ClearListings();
- foreach (var item in listings)
- {
- AddListingGui(item);
- }
- }
-
- private void OnWithdrawButtonDown(BaseButton.ButtonEventArgs args)
- {
- if (_loggedInUplinkAccount == null)
- return;
-
- // check if window is already open
- if (_withdrawWindow != null && _withdrawWindow.IsOpen)
- {
- _withdrawWindow.MoveToFront();
- return;
- }
-
- // open a new one
- _withdrawWindow = new UplinkWithdrawWindow(_loggedInUplinkAccount.DataBalance);
- _withdrawWindow.OpenCentered();
-
- _withdrawWindow.OnWithdrawAttempt += OnWithdrawAttempt;
- }
-
- private void AddListingGui(UplinkListingData listing)
- {
- if (!_prototypeManager.TryIndex(listing.ItemId, out EntityPrototype? prototype) || listing.Category != CurrentFilterCategory)
- {
- return;
- }
-
- var listingName = listing.ListingName == string.Empty ? prototype.Name : listing.ListingName;
- var listingDesc = listing.Description == string.Empty ? prototype.Description : listing.Description;
- var listingPrice = listing.Price;
- var canBuy = _loggedInUplinkAccount?.DataBalance >= listing.Price;
-
- var texture = listing.Icon?.Frame0();
- if (texture == null)
- texture = SpriteComponent.GetPrototypeIcon(prototype, _resourceCache).Default;
-
- var newListing = new UplinkListingControl(listingName, listingDesc, listingPrice, canBuy, texture);
- newListing.UplinkItemBuyButton.OnButtonDown += args
- => OnListingButtonPressed?.Invoke(args, listing);
-
- UplinkListingsContainer.AddChild(newListing);
- }
-
- private void ClearListings()
- {
- UplinkListingsContainer.Children.Clear();
- }
-
- private void PopulateUplinkCategoryButtons()
- {
- foreach (UplinkCategory cat in Enum.GetValues(typeof(UplinkCategory)))
- {
- var catButton = new PDAUplinkCategoryButton
- {
- Text = Loc.GetString(cat.ToString()),
- ButtonCategory = cat
- };
- //It'd be neat if it could play a cool tech ping sound when you switch categories,
- //but right now there doesn't seem to be an easy way to do client-side audio without still having to round trip to the server and
- //send to a specific client INetChannel.
- catButton.OnPressed += args => OnCategoryButtonPressed?.Invoke(args, catButton.ButtonCategory);
-
- CategoryListContainer.AddChild(catButton);
- }
- }
-
- public override void Close()
- {
- base.Close();
- _withdrawWindow?.Close();
- }
-
- private sealed class PDAUplinkCategoryButton : Button
- {
- public UplinkCategory ButtonCategory;
- }
- }
-}
diff --git a/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml b/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml
deleted file mode 100644
index 92d94e112d..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml.cs b/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml.cs
deleted file mode 100644
index d83546dd55..0000000000
--- a/Content.Client/Traitor/Uplink/UplinkWithdrawWindow.xaml.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Localization;
-
-namespace Content.Client.Traitor.Uplink
-{
- ///
- /// Window to select amount TC to withdraw from Uplink account
- /// Used as sub-window in Uplink UI
- ///
- [GenerateTypedNameReferences]
- public sealed partial class UplinkWithdrawWindow : DefaultWindow
- {
- public event System.Action? OnWithdrawAttempt;
-
- public UplinkWithdrawWindow(int tcCount)
- {
- RobustXamlLoader.Load(this);
-
- // setup withdraw slider
- WithdrawSlider.MinValue = 1;
- WithdrawSlider.MaxValue = tcCount;
-
- // and buttons
- ApplyButton.OnButtonDown += _ =>
- {
- OnWithdrawAttempt?.Invoke(WithdrawSlider.Value);
- Close();
- };
- CancelButton.OnButtonDown += _ => Close();
- }
- }
-}
diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
index 71c7d46664..3ca27a4a46 100644
--- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
@@ -20,6 +20,8 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Content.Server.Traitor;
+using System.Data;
+using Content.Server.Traitor.Uplink;
using Robust.Shared.Audio;
using Robust.Shared.Player;
@@ -35,6 +37,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawningSystem = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
+ [Dependency] private readonly UplinkSystem _uplink = default!;
private Dictionary _aliveNukeops = new();
private bool _opsWon;
diff --git a/Content.Server/GameTicking/Rules/SuspicionRuleSystem.cs b/Content.Server/GameTicking/Rules/SuspicionRuleSystem.cs
index c60da5e71c..d77d5f9d2b 100644
--- a/Content.Server/GameTicking/Rules/SuspicionRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/SuspicionRuleSystem.cs
@@ -8,7 +8,6 @@ using Content.Server.Station.Components;
using Content.Server.Suspicion;
using Content.Server.Suspicion.Roles;
using Content.Server.Traitor.Uplink;
-using Content.Server.Traitor.Uplink.Account;
using Content.Shared.CCVar;
using Content.Shared.Doors.Systems;
using Content.Shared.EntityList;
@@ -17,7 +16,6 @@ using Content.Shared.Maps;
using Content.Shared.MobState.Components;
using Content.Shared.Roles;
using Content.Shared.Suspicion;
-using Content.Shared.Traitor.Uplink;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
@@ -48,6 +46,7 @@ public sealed class SuspicionRuleSystem : GameRuleSystem
[Dependency] private readonly ITileDefinitionManager _tileDefMan = default!;
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookupSystem = default!;
+ [Dependency] private readonly UplinkSystem _uplink = default!;
public override string Prototype => "Suspicion";
@@ -173,16 +172,8 @@ public sealed class SuspicionRuleSystem : GameRuleSystem
mind!.AddRole(traitorRole);
traitors.Add(traitorRole);
- // creadth: we need to create uplink for the antag.
- // PDA should be in place already, so we just need to
- // initiate uplink account.
- var uplinkAccount = new UplinkAccount(traitorStartingBalance, mind.OwnedEntity!);
- var accounts = EntityManager.EntitySysManager.GetEntitySystem();
- accounts.AddNewAccount(uplinkAccount);
-
// try to place uplink
- if (!EntityManager.EntitySysManager.GetEntitySystem()
- .AddUplink(mind.OwnedEntity!.Value, uplinkAccount))
+ if (!_uplink.AddUplink(mind.OwnedEntity!.Value, traitorStartingBalance))
continue;
}
diff --git a/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs
index 92b6913fc1..1cd87250c4 100644
--- a/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking.Rules.Configurations;
@@ -6,19 +6,18 @@ using Content.Server.Hands.Components;
using Content.Server.PDA;
using Content.Server.Players;
using Content.Server.Spawners.Components;
+using Content.Server.Store.Components;
using Content.Server.Traitor;
using Content.Server.Traitor.Uplink;
-using Content.Server.Traitor.Uplink.Account;
-using Content.Server.Traitor.Uplink.Components;
using Content.Server.TraitorDeathMatch.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.MobState.Components;
using Content.Shared.PDA;
using Content.Shared.Roles;
-using Content.Shared.Traitor.Uplink;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Configuration;
@@ -39,6 +38,7 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
+ [Dependency] private readonly UplinkSystem _uplink = default!;
public override string Prototype => "TraitorDeathMatch";
@@ -48,7 +48,7 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
private bool _safeToEndRound = false;
- private readonly Dictionary _allOriginalNames = new();
+ private readonly Dictionary _allOriginalNames = new();
private const string TraitorPrototypeID = "Traitor";
@@ -108,15 +108,10 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
newTmp = Spawn(BackpackPrototypeName, ownedCoords);
_inventory.TryEquip(owned, newTmp, "back", true);
- // Like normal traitors, they need access to a traitor account.
- var uplinkAccount = new UplinkAccount(startingBalance, owned);
- var accounts = EntityManager.EntitySysManager.GetEntitySystem();
- accounts.AddNewAccount(uplinkAccount);
+ if (!_uplink.AddUplink(owned, startingBalance))
+ return;
- EntityManager.EntitySysManager.GetEntitySystem()
- .AddUplink(owned, uplinkAccount, newPDA);
-
- _allOriginalNames[uplinkAccount] = Name(owned);
+ _allOriginalNames[owned] = Name(owned);
// The PDA needs to be marked with the correct owner.
var pda = Comp(newPDA);
@@ -186,14 +181,17 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
var lines = new List();
lines.Add(Loc.GetString("traitor-death-match-end-round-description-first-line"));
- foreach (var uplink in EntityManager.EntityQuery(true))
+
+ foreach (var uplink in EntityManager.EntityQuery(true))
{
- var uplinkAcc = uplink.UplinkAccount;
- if (uplinkAcc != null && _allOriginalNames.ContainsKey(uplinkAcc))
+ var owner = uplink.AccountOwner;
+ if (owner != null && _allOriginalNames.ContainsKey(owner.Value))
{
+ var tcbalance = _uplink.GetTCBalance(uplink);
+
lines.Add(Loc.GetString("traitor-death-match-end-round-description-entry",
- ("originalName", _allOriginalNames[uplinkAcc]),
- ("tcBalance", uplinkAcc.Balance)));
+ ("originalName", _allOriginalNames[owner.Value]),
+ ("tcBalance", tcbalance)));
}
}
diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
index 030e1c2300..29b7b91895 100644
--- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
@@ -3,13 +3,14 @@ using Content.Server.Chat.Managers;
using Content.Server.Objectives.Interfaces;
using Content.Server.Players;
using Content.Server.Roles;
+using Content.Server.Store.Systems;
using Content.Server.Traitor;
using Content.Server.Traitor.Uplink;
-using Content.Server.Traitor.Uplink.Account;
using Content.Shared.CCVar;
using Content.Shared.Dataset;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Inventory;
using Content.Shared.Roles;
-using Content.Shared.Traitor.Uplink;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
@@ -28,6 +29,10 @@ public sealed class TraitorRuleSystem : GameRuleSystem
[Dependency] private readonly IObjectivesManager _objectivesManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
+ [Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly StoreSystem _store = default!;
+ [Dependency] private readonly UplinkSystem _uplink = default!;
public override string Prototype => "Traitor";
@@ -35,6 +40,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem
public List Traitors = new();
private const string TraitorPrototypeID = "Traitor";
+ private const string TraitorUplinkPresetId = "StorePresetUplink";
public int TotalTraitors => Traitors.Count;
public string[] Codewords = new string[3];
@@ -173,16 +179,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem
}
// creadth: we need to create uplink for the antag.
- // PDA should be in place already, so we just need to
- // initiate uplink account.
+ // PDA should be in place already
DebugTools.AssertNotNull(mind.OwnedEntity);
var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance);
- var uplinkAccount = new UplinkAccount(startingBalance, mind.OwnedEntity!);
- var accounts = EntityManager.EntitySysManager.GetEntitySystem();
- accounts.AddNewAccount(uplinkAccount);
- if (!EntityManager.EntitySysManager.GetEntitySystem().AddUplink(mind.OwnedEntity!.Value, uplinkAccount))
+ if (!_uplink.AddUplink(mind.OwnedEntity!.Value, startingBalance))
return false;
var antagPrototype = _prototypeManager.Index(TraitorPrototypeID);
diff --git a/Content.Server/PDA/PDASystem.cs b/Content.Server/PDA/PDASystem.cs
index 3421380f34..84d0e95392 100644
--- a/Content.Server/PDA/PDASystem.cs
+++ b/Content.Server/PDA/PDASystem.cs
@@ -2,29 +2,28 @@ using Content.Server.Instruments;
using Content.Server.Light.Components;
using Content.Server.Light.EntitySystems;
using Content.Server.Light.Events;
-using Content.Server.Traitor.Uplink;
-using Content.Server.Traitor.Uplink.Account;
-using Content.Server.Traitor.Uplink.Components;
using Content.Server.PDA.Ringer;
-using Content.Server.Station.Components;
+using Content.Server.Store.Components;
+using Content.Server.Store.Systems;
using Content.Server.Station.Systems;
using Content.Server.UserInterface;
using Content.Shared.PDA;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.Map;
+using Content.Server.Mind.Components;
+using Content.Server.Traitor;
namespace Content.Server.PDA
{
public sealed class PDASystem : SharedPDASystem
{
- [Dependency] private readonly UplinkSystem _uplinkSystem = default!;
- [Dependency] private readonly UplinkAccountsSystem _uplinkAccounts = default!;
[Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
[Dependency] private readonly RingerSystem _ringerSystem = default!;
[Dependency] private readonly InstrumentSystem _instrumentSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
+ [Dependency] private readonly StoreSystem _storeSystem = default!;
public override void Initialize()
{
@@ -32,8 +31,8 @@ namespace Content.Server.PDA
SubscribeLocalEvent(OnLightToggle);
SubscribeLocalEvent(AfterUIOpen);
- SubscribeLocalEvent(OnUplinkInit);
- SubscribeLocalEvent(OnUplinkRemoved);
+ SubscribeLocalEvent(OnUplinkInit);
+ SubscribeLocalEvent(OnUplinkRemoved);
SubscribeLocalEvent(OnGridChanged);
}
@@ -74,12 +73,12 @@ namespace Content.Server.PDA
UpdatePDAUserInterface(pda);
}
- private void OnUplinkInit(EntityUid uid, PDAComponent pda, UplinkInitEvent args)
+ private void OnUplinkInit(EntityUid uid, PDAComponent pda, StoreAddedEvent args)
{
UpdatePDAUserInterface(pda);
}
- private void OnUplinkRemoved(EntityUid uid, PDAComponent pda, UplinkRemovedEvent args)
+ private void OnUplinkRemoved(EntityUid uid, PDAComponent pda, StoreRemovedEvent args)
{
UpdatePDAUserInterface(pda);
}
@@ -111,7 +110,7 @@ namespace Content.Server.PDA
// players. This should really use a sort of key-code entry system that selects an account which is not directly tied to
// a player entity.
- if (!HasComp(pda.Owner))
+ if (!TryComp(pda.Owner, out var storeComponent))
return;
var uplinkState = new PDAUpdateState(pda.FlashlightOn, pda.PenSlot.HasItem, ownerInfo, pda.StationName, true, hasInstrument);
@@ -121,7 +120,8 @@ namespace Content.Server.PDA
if (session.AttachedEntity is not EntityUid { Valid: true } user)
continue;
- if (_uplinkAccounts.HasAccount(user))
+ if (storeComponent.AccountOwner == user || (TryComp(session.AttachedEntity, out var mindcomp) && mindcomp.Mind != null &&
+ mindcomp.Mind.HasRole()))
ui.SetState(uplinkState, session);
}
}
@@ -143,8 +143,9 @@ namespace Content.Server.PDA
case PDAShowUplinkMessage _:
{
- if (EntityManager.TryGetComponent(pda.Owner, out UplinkComponent? uplink))
- _uplinkSystem.ToggleUplinkUI(uplink, msg.Session);
+ if (msg.Session.AttachedEntity != null &&
+ TryComp(pda.Owner, out var store))
+ _storeSystem.ToggleUi(msg.Session.AttachedEntity.Value, store);
break;
}
case PDAShowRingtoneMessage _:
@@ -170,8 +171,13 @@ namespace Content.Server.PDA
private void AfterUIOpen(EntityUid uid, PDAComponent pda, AfterActivatableUIOpenEvent args)
{
+ //TODO: this is awful
// A new user opened the UI --> Check if they are a traitor and should get a user specific UI state override.
- if (!HasComp(pda.Owner) || !_uplinkAccounts.HasAccount(args.User))
+ if (!TryComp(pda.Owner, out var storeComp))
+ return;
+
+ if (storeComp.AccountOwner != args.User &&
+ !(TryComp(args.User, out var mindcomp) && mindcomp.Mind != null && mindcomp.Mind.HasRole()))
return;
if (!_uiSystem.TryGetUi(pda.Owner, PDAUiKey.Key, out var ui))
diff --git a/Content.Server/Store/Components/CurrencyComponent.cs b/Content.Server/Store/Components/CurrencyComponent.cs
new file mode 100644
index 0000000000..872a995bde
--- /dev/null
+++ b/Content.Server/Store/Components/CurrencyComponent.cs
@@ -0,0 +1,22 @@
+using Content.Shared.FixedPoint;
+using Content.Shared.Store;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
+
+namespace Content.Server.Store.Components;
+
+///
+/// Identifies a component that can be inserted into a store
+/// to increase its balance.
+///
+[RegisterComponent]
+public sealed class CurrencyComponent : Component
+{
+ ///
+ /// The value of the currency.
+ /// The string is the currency type that will be added.
+ /// The FixedPoint2 is the value of each individual currency entity.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("price", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
+ public Dictionary Price = new();
+}
diff --git a/Content.Server/Store/Components/StoreComponent.cs b/Content.Server/Store/Components/StoreComponent.cs
new file mode 100644
index 0000000000..10b46e3e05
--- /dev/null
+++ b/Content.Server/Store/Components/StoreComponent.cs
@@ -0,0 +1,91 @@
+using Content.Shared.FixedPoint;
+using Content.Shared.Store;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Audio;
+
+namespace Content.Server.Store.Components;
+
+///
+/// This component manages a store which players can use to purchase different listings
+/// through the ui. The currency, listings, and categories are defined in yaml.
+///
+[RegisterComponent]
+public sealed class StoreComponent : Component
+{
+ ///
+ /// The default preset for the store. Is overriden by default values specified on the component.
+ ///
+ [DataField("preset", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? Preset;
+
+ ///
+ /// All the listing categories that are available on this store.
+ /// The available listings are partially based on the categories.
+ ///
+ [DataField("categories", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet Categories = new();
+
+ ///
+ /// The total amount of currency that can be used in the store.
+ /// The string represents the ID of te currency prototype, where the
+ /// float is that amount.
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField("balance", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
+ public Dictionary Balance = new();
+
+ ///
+ /// The list of currencies that can be inserted into this store.
+ ///
+ [ViewVariables(VVAccess.ReadOnly), DataField("currencyWhitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet CurrencyWhitelist = new();
+
+ ///
+ /// The person who "owns" the store/account. Used if you want the listings to be fixed
+ /// regardless of who activated it. I.E. role specific items for uplinks.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ public EntityUid? AccountOwner = null;
+
+ ///
+ /// All listings, including those that aren't available to the buyer
+ ///
+ public HashSet Listings = new();
+
+ ///
+ /// All available listings from the last time that it was checked.
+ ///
+ [ViewVariables]
+ public HashSet LastAvailableListings = new();
+
+ ///
+ /// checks whether or not the store has been opened yet.
+ ///
+ public bool Opened = false;
+
+ #region audio
+ ///
+ /// The sound played to the buyer when a purchase is succesfully made.
+ ///
+ [ViewVariables]
+ [DataField("buySuccessSound")]
+ public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg");
+
+ ///
+ /// The sound played to the buyer when a purchase fails.
+ ///
+ [ViewVariables]
+ [DataField("insufficientFundsSound")]
+ public SoundSpecifier InsufficientFundsSound = new SoundPathSpecifier("/Audio/Effects/error.ogg");
+ #endregion
+}
+
+///
+/// Event that is broadcast when a store is added to an entity
+///
+public sealed class StoreAddedEvent : EntityEventArgs { };
+///
+/// Event that is broadcast when a store is removed from an entity
+///
+public sealed class StoreRemovedEvent : EntityEventArgs { };
diff --git a/Content.Server/Store/Conditions/BuyerAntagCondition.cs b/Content.Server/Store/Conditions/BuyerAntagCondition.cs
new file mode 100644
index 0000000000..d0f4292dbe
--- /dev/null
+++ b/Content.Server/Store/Conditions/BuyerAntagCondition.cs
@@ -0,0 +1,63 @@
+using Content.Server.Mind.Components;
+using Content.Server.Traitor;
+using Content.Shared.Roles;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+
+namespace Content.Shared.Store.Conditions;
+
+///
+/// Allows a store entry to be filtered out based on the user's antag role.
+/// Supports both blacklists and whitelists. This is copypaste because roles
+/// are absolute shitcode. Refactor this later. -emo
+///
+public sealed class BuyerAntagCondition : ListingCondition
+{
+ ///
+ /// A whitelist of antag roles that can purchase this listing. Only one needs to be found.
+ ///
+ [DataField("whitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet? Whitelist;
+
+ ///
+ /// A blacklist of antag roles that cannot purchase this listing. Only one needs to be found.
+ ///
+ [DataField("blacklist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet? Blacklist;
+
+ public override bool Condition(ListingConditionArgs args)
+ {
+ var ent = args.EntityManager;
+
+ if (!ent.TryGetComponent(args.Buyer, out var mind) || mind.Mind == null)
+ return true;
+
+ if (Blacklist != null)
+ {
+ foreach (var role in mind.Mind.AllRoles)
+ {
+ if (role is not TraitorRole blacklistantag)
+ continue;
+
+ if (Blacklist.Contains(blacklistantag.Prototype.ID))
+ return false;
+ }
+ }
+
+ if (Whitelist != null)
+ {
+ var found = false;
+ foreach (var role in mind.Mind.AllRoles)
+ {
+ if (role is not TraitorRole antag)
+ continue;
+
+ if (Whitelist.Contains(antag.Prototype.ID))
+ found = true;
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/Content.Server/Store/Conditions/BuyerJobCondition.cs b/Content.Server/Store/Conditions/BuyerJobCondition.cs
new file mode 100644
index 0000000000..d867a65d33
--- /dev/null
+++ b/Content.Server/Store/Conditions/BuyerJobCondition.cs
@@ -0,0 +1,63 @@
+using Content.Server.Mind.Components;
+using Content.Server.Roles;
+using Content.Shared.Roles;
+using Content.Shared.Store;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+
+namespace Content.Server.Store.Conditions;
+
+///
+/// Allows a store entry to be filtered out based on the user's job.
+/// Supports both blacklists and whitelists
+///
+public sealed class BuyerJobCondition : ListingCondition
+{
+ ///
+ /// A whitelist of jobs prototypes that can purchase this listing. Only one needs to be found.
+ ///
+ [DataField("whitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet? Whitelist;
+
+ ///
+ /// A blacklist of job prototypes that can purchase this listing. Only one needs to be found.
+ ///
+ [DataField("blacklist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet? Blacklist;
+
+ public override bool Condition(ListingConditionArgs args)
+ {
+ var ent = args.EntityManager;
+
+ if (!ent.TryGetComponent(args.Buyer, out var mind) || mind.Mind == null)
+ return true; //this is for things like surplus crate
+
+ if (Blacklist != null)
+ {
+ foreach (var role in mind.Mind.AllRoles)
+ {
+ if (role is not Job job)
+ continue;
+
+ if (Blacklist.Contains(job.Prototype.ID))
+ return false;
+ }
+ }
+
+ if (Whitelist != null)
+ {
+ var found = false;
+ foreach (var role in mind.Mind.AllRoles)
+ {
+ if (role is not Job job)
+ continue;
+
+ if (Whitelist.Contains(job.Prototype.ID))
+ found = true;
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs b/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs
new file mode 100644
index 0000000000..e9ca245ccd
--- /dev/null
+++ b/Content.Server/Store/Conditions/BuyerWhitelistCondition.cs
@@ -0,0 +1,41 @@
+using Content.Shared.Store;
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Store.Conditions;
+
+///
+/// Filters out an entry based on the components or tags on an entity.
+///
+public sealed class BuyerWhitelistCondition : ListingCondition
+{
+ ///
+ /// A whitelist of tags or components.
+ ///
+ [DataField("whitelist")]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// A blacklist of tags or components.
+ ///
+ [DataField("blacklist")]
+ public EntityWhitelist? Blacklist;
+
+ public override bool Condition(ListingConditionArgs args)
+ {
+ var ent = args.EntityManager;
+
+ if (Whitelist != null)
+ {
+ if (!Whitelist.IsValid(args.Buyer, ent))
+ return false;
+ }
+
+ if (Blacklist != null)
+ {
+ if (Blacklist.IsValid(args.Buyer, ent))
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/Content.Server/Store/Conditions/ListingLimitedStockCondition.cs b/Content.Server/Store/Conditions/ListingLimitedStockCondition.cs
new file mode 100644
index 0000000000..e1fdbfe892
--- /dev/null
+++ b/Content.Server/Store/Conditions/ListingLimitedStockCondition.cs
@@ -0,0 +1,20 @@
+using Content.Shared.Store;
+
+namespace Content.Server.Store.Conditions;
+
+///
+/// Only allows a listing to be purchased a certain amount of times.
+///
+public sealed class ListingLimitedStockCondition : ListingCondition
+{
+ ///
+ /// The amount of times this listing can be purchased.
+ ///
+ [DataField("stock", required: true)]
+ public int Stock;
+
+ public override bool Condition(ListingConditionArgs args)
+ {
+ return args.Listing.PurchaseAmount < Stock;
+ }
+}
diff --git a/Content.Server/Store/Conditions/StoreWhitelistCondition.cs b/Content.Server/Store/Conditions/StoreWhitelistCondition.cs
new file mode 100644
index 0000000000..ccef958320
--- /dev/null
+++ b/Content.Server/Store/Conditions/StoreWhitelistCondition.cs
@@ -0,0 +1,44 @@
+using Content.Shared.Store;
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Store.Conditions;
+
+///
+/// Filters out an entry based on the components or tags on the store itself.
+///
+public sealed class StoreWhitelistCondition : ListingCondition
+{
+ ///
+ /// A whitelist of tags or components.
+ ///
+ [DataField("whitelist")]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// A blacklist of tags or components.
+ ///
+ [DataField("blacklist")]
+ public EntityWhitelist? Blacklist;
+
+ public override bool Condition(ListingConditionArgs args)
+ {
+ if (args.StoreEntity == null)
+ return false;
+
+ var ent = args.EntityManager;
+
+ if (Whitelist != null)
+ {
+ if (!Whitelist.IsValid(args.StoreEntity.Value, ent))
+ return false;
+ }
+
+ if (Blacklist != null)
+ {
+ if (Blacklist.IsValid(args.StoreEntity.Value, ent))
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs
new file mode 100644
index 0000000000..36b1a4b8c5
--- /dev/null
+++ b/Content.Server/Store/Systems/StoreSystem.Listings.cs
@@ -0,0 +1,127 @@
+using Content.Server.Store.Components;
+using Content.Shared.Store;
+
+namespace Content.Server.Store.Systems;
+
+public sealed partial class StoreSystem : EntitySystem
+{
+ ///
+ /// Refreshes all listings on a store.
+ /// Do not use if you don't know what you're doing.
+ ///
+ /// The store to refresh
+ public void RefreshAllListings(StoreComponent component)
+ {
+ component.Listings = GetAllListings();
+ }
+
+ ///
+ /// Gets all listings from a prototype.
+ ///
+ /// All the listings
+ public HashSet GetAllListings()
+ {
+ var allListings = _proto.EnumeratePrototypes();
+
+ var allData = new HashSet();
+
+ foreach (var listing in allListings)
+ allData.Add(listing);
+
+ return allData;
+ }
+
+ ///
+ /// Adds a listing from an Id to a store
+ ///
+ /// The store to add the listing to
+ /// The id of the listing
+ /// Whetehr or not the listing was added successfully
+ public bool TryAddListing(StoreComponent component, string listingId)
+ {
+ if (!_proto.TryIndex(listingId, out var proto))
+ {
+ Logger.Error("Attempted to add invalid listing.");
+ return false;
+ }
+ return TryAddListing(component, proto);
+ }
+
+ ///
+ /// Adds a listing to a store
+ ///
+ /// The store to add the listing to
+ /// The listing
+ /// Whether or not the listing was add successfully
+ public bool TryAddListing(StoreComponent component, ListingData listing)
+ {
+ return component.Listings.Add(listing);
+ }
+
+ ///
+ /// Gets the available listings for a store
+ ///
+ /// The person getting the listings.
+ /// The store the listings are coming from.
+ /// The available listings.
+ public IEnumerable GetAvailableListings(EntityUid user, StoreComponent component)
+ {
+ return GetAvailableListings(user, component.Listings, component.Categories, component.Owner);
+ }
+
+ ///
+ /// Gets the available listings for a user given an overall set of listings and categories to filter by.
+ ///
+ /// The person getting the listings.
+ /// All of the listings that are available. If null, will just get all listings from the prototypes.
+ /// What categories to filter by.
+ /// The physial entity of the store. Can be null.
+ /// The available listings.
+ public IEnumerable GetAvailableListings(EntityUid user, HashSet? listings, HashSet categories, EntityUid? storeEntity = null)
+ {
+ if (listings == null)
+ listings = GetAllListings();
+
+ foreach (var listing in listings)
+ {
+ if (!ListingHasCategory(listing, categories))
+ continue;
+
+ if (listing.Conditions != null)
+ {
+ var args = new ListingConditionArgs(user, storeEntity, listing, EntityManager);
+ var conditionsMet = true;
+
+ foreach (var condition in listing.Conditions)
+ {
+ if (!condition.Condition(args))
+ {
+ conditionsMet = false;
+ break;
+ }
+ }
+
+ if (!conditionsMet)
+ continue;
+ }
+
+ yield return listing;
+ }
+ }
+
+ ///
+ /// Checks if a listing appears in a list of given categories
+ ///
+ /// The listing itself.
+ /// The categories to check through.
+ /// If the listing was present in one of the categories.
+ public bool ListingHasCategory(ListingData listing, HashSet categories)
+ {
+ foreach (var cat in categories)
+ {
+ if (listing.Categories.Contains(cat))
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs
new file mode 100644
index 0000000000..b8514deda2
--- /dev/null
+++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs
@@ -0,0 +1,226 @@
+using Content.Server.Actions;
+using Content.Server.Administration.Logs;
+using Content.Server.Mind.Components;
+using Content.Server.Store.Components;
+using Content.Server.UserInterface;
+using Content.Shared.Actions.ActionTypes;
+using Content.Shared.FixedPoint;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Store;
+using Content.Shared.Database;
+using Robust.Server.GameObjects;
+using System.Linq;
+using Content.Server.Stack;
+using Content.Shared.Prototypes;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Store.Systems;
+
+public sealed partial class StoreSystem : EntitySystem
+{
+ [Dependency] private readonly IAdminLogManager _admin = default!;
+ [Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly ActionsSystem _actions = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly StackSystem _stack = default!;
+
+ private void InitializeUi()
+ {
+ SubscribeLocalEvent((_,c,r) => UpdateUserInterface(r.CurrentBuyer, c));
+ SubscribeLocalEvent(OnBuyRequest);
+ SubscribeLocalEvent(OnRequestWithdraw);
+ }
+
+ ///
+ /// Toggles the store Ui open and closed
+ ///
+ /// the person doing the toggling
+ /// the store being toggled
+ public void ToggleUi(EntityUid user, StoreComponent component)
+ {
+ if (!TryComp(user, out var actor))
+ return;
+
+ var ui = component.Owner.GetUIOrNull(StoreUiKey.Key);
+ ui?.Toggle(actor.PlayerSession);
+
+ UpdateUserInterface(user, component, ui);
+ }
+
+ ///
+ /// Updates the user interface for a store and refreshes the listings
+ ///
+ /// The person who if opening the store ui. Listings are filtered based on this.
+ /// The store component being refreshed.
+ ///
+ public void UpdateUserInterface(EntityUid? user, StoreComponent component, BoundUserInterface? ui = null)
+ {
+ if (ui == null)
+ {
+ ui = component.Owner.GetUIOrNull(StoreUiKey.Key);
+ if (ui == null)
+ {
+ Logger.Error("No Ui key.");
+ return;
+ }
+ }
+
+ //if we haven't opened it before, initialize the shit
+ if (!component.Opened)
+ {
+ InitializeFromPreset(component.Preset, component);
+ component.Opened = true;
+ }
+
+ //this is the person who will be passed into logic for all listing filtering.
+ var buyer = user;
+ if (buyer != null) //if we have no "buyer" for this update, then don't update the listings
+ {
+ if (component.AccountOwner != null) //if we have one stored, then use that instead
+ buyer = component.AccountOwner.Value;
+
+ component.LastAvailableListings = GetAvailableListings(buyer.Value, component).ToHashSet();
+ }
+
+ //dictionary for all currencies, including 0 values for currencies on the whitelist
+ Dictionary allCurrency = new();
+ foreach (var supported in component.CurrencyWhitelist)
+ {
+ allCurrency.Add(supported, FixedPoint2.Zero);
+
+ if (component.Balance.ContainsKey(supported))
+ allCurrency[supported] = component.Balance[supported];
+ }
+
+ var state = new StoreUpdateState(buyer, component.LastAvailableListings, allCurrency);
+ ui.SetState(state);
+ }
+
+ ///
+ /// Handles whenever a purchase was made.
+ ///
+ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListingMessage msg)
+ {
+ ListingData? listing = component.Listings.FirstOrDefault(x => x.Equals(msg.Listing));
+ if (listing == null) //make sure this listing actually exists
+ {
+ Logger.Debug("listing does not exist");
+ return;
+ }
+
+ //verify that we can actually buy this listing and it wasn't added
+ if (!ListingHasCategory(listing, component.Categories))
+ return;
+ //condition checking because why not
+ if (listing.Conditions != null)
+ {
+ var args = new ListingConditionArgs(msg.Buyer, component.Owner, listing, EntityManager);
+ var conditionsMet = true;
+
+ foreach (var condition in listing.Conditions.Where(condition => !condition.Condition(args)))
+ conditionsMet = false;
+
+ if (!conditionsMet)
+ return;
+ }
+
+ //check that we have enough money
+ foreach (var currency in listing.Cost)
+ {
+ if (!component.Balance.TryGetValue(currency.Key, out var balance) || balance < currency.Value)
+ {
+ _audio.Play(component.InsufficientFundsSound, Filter.SinglePlayer(msg.Session), uid);
+ return;
+ }
+ }
+ //subtract the cash
+ foreach (var currency in listing.Cost)
+ component.Balance[currency.Key] -= currency.Value;
+
+ //spawn entity
+ if (listing.ProductEntity != null)
+ {
+ var product = Spawn(listing.ProductEntity, Transform(msg.Buyer).Coordinates);
+ _hands.TryPickupAnyHand(msg.Buyer, product);
+ }
+
+ //give action
+ if (listing.ProductAction != null)
+ {
+ var action = new InstantAction(_proto.Index(listing.ProductAction));
+ _actions.AddAction(msg.Buyer, action, null);
+ }
+
+ //broadcast event
+ if (listing.ProductEvent != null)
+ {
+ RaiseLocalEvent(listing.ProductEvent);
+ }
+
+ //log dat shit.
+ if (TryComp(msg.Buyer, out var mind))
+ {
+ _admin.Add(LogType.StorePurchase, LogImpact.Low,
+ $"{ToPrettyString(mind.Owner):player} purchased listing \"{listing.Name}\" from {ToPrettyString(uid)}");
+ }
+
+ listing.PurchaseAmount++; //track how many times something has been purchased
+ _audio.Play(component.BuySuccessSound, Filter.SinglePlayer(msg.Session), uid); //cha-ching!
+
+ UpdateUserInterface(msg.Buyer, component);
+ }
+
+ ///
+ /// Handles dispensing the currency you requested to be withdrawn.
+ ///
+ ///
+ /// This would need to be done should a currency with decimal values need to use it.
+ /// not quite sure how to handle that
+ ///
+ private void OnRequestWithdraw(EntityUid uid, StoreComponent component, StoreRequestWithdrawMessage msg)
+ {
+ //make sure we have enough cash in the bank and we actually support this currency
+ if (!component.Balance.TryGetValue(msg.Currency, out var currentAmount) || currentAmount < msg.Amount)
+ return;
+
+ //make sure a malicious client didn't send us random shit
+ if (!_proto.TryIndex(msg.Currency, out var proto))
+ return;
+
+ //we need an actually valid entity to spawn. This check has been done earlier, but just in case.
+ if (proto.EntityId == null || !proto.CanWithdraw)
+ return;
+
+ var entproto = _proto.Index(proto.EntityId);
+
+ var amountRemaining = msg.Amount;
+ var coordinates = Transform(msg.Buyer).Coordinates;
+ if (entproto.HasComponent())
+ {
+ while (amountRemaining > 0)
+ {
+ var ent = Spawn(proto.EntityId, coordinates);
+ var stackComponent = Comp(ent); //we already know it exists
+
+ var amountPerStack = Math.Min(stackComponent.MaxCount, amountRemaining);
+
+ _stack.SetCount(ent, amountPerStack, stackComponent);
+ amountRemaining -= amountPerStack;
+ _hands.TryPickupAnyHand(msg.Buyer, ent);
+ }
+ }
+ else //please for the love of christ give your currency stack component
+ {
+ while (amountRemaining > 0)
+ {
+ var ent = Spawn(proto.EntityId, coordinates);
+ _hands.TryPickupAnyHand(msg.Buyer, ent);
+ amountRemaining--;
+ }
+ }
+
+ component.Balance[msg.Currency] -= msg.Amount;
+ UpdateUserInterface(msg.Buyer, component);
+ }
+}
diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs
new file mode 100644
index 0000000000..dfa2c16dfc
--- /dev/null
+++ b/Content.Server/Store/Systems/StoreSystem.cs
@@ -0,0 +1,154 @@
+using Content.Server.Stack;
+using Content.Server.Store.Components;
+using Content.Shared.FixedPoint;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.Store;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+using System.Linq;
+using Content.Server.UserInterface;
+
+namespace Content.Server.Store.Systems;
+
+///
+/// Manages general interactions with a store and different entities,
+/// getting listings for stores, and interfacing with the store UI.
+///
+public sealed partial class StoreSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnAfterInteract);
+ SubscribeLocalEvent((_,c,a) => UpdateUserInterface(a.User, c));
+
+ SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnShutdown);
+
+ InitializeUi();
+ }
+
+ private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args)
+ {
+ RaiseLocalEvent(uid, new StoreAddedEvent(), true);
+ }
+
+ private void OnShutdown(EntityUid uid, StoreComponent component, ComponentShutdown args)
+ {
+ RaiseLocalEvent(uid, new StoreRemovedEvent(), true);
+ }
+
+ private void OnAfterInteract(EntityUid uid, CurrencyComponent component, AfterInteractEvent args)
+ {
+ if (args.Handled || !args.CanReach)
+ return;
+
+ if (args.Target == null || !TryComp(args.Target, out var store))
+ return;
+
+ //if you somehow are inserting cash before the store initializes.
+ if (!store.Opened)
+ {
+ InitializeFromPreset(store.Preset, store);
+ store.Opened = true;
+ }
+
+ args.Handled = TryAddCurrency(GetCurrencyValue(component), store);
+
+ if (args.Handled)
+ {
+ var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", args.Target));
+ _popup.PopupEntity(msg, args.Target.Value, Filter.Pvs(args.Target.Value));
+ QueueDel(args.Used);
+ }
+ }
+
+ ///
+ /// Gets the value from an entity's currency component.
+ /// Scales with stacks.
+ ///
+ ///
+ /// The value of the currency
+ public Dictionary GetCurrencyValue(CurrencyComponent component)
+ {
+ TryComp(component.Owner, out var stack);
+ var amount = stack?.Count ?? 1;
+
+ return component.Price.ToDictionary(v => v.Key, p => p.Value * amount);
+ }
+
+ ///
+ /// Tries to add a currency to a store's balance.
+ ///
+ /// The currency to add
+ /// The store to add it to
+ /// Whether or not the currency was succesfully added
+ public bool TryAddCurrency(CurrencyComponent component, StoreComponent store)
+ {
+ return TryAddCurrency(GetCurrencyValue(component), store);
+ }
+
+ ///
+ /// Tries to add a currency to a store's balance
+ ///
+ /// The value to add to the store
+ /// The store to add it to
+ /// Whether or not the currency was succesfully added
+ public bool TryAddCurrency(Dictionary currency, StoreComponent store)
+ {
+ //verify these before values are modified
+ foreach (var type in currency)
+ {
+ if (!store.CurrencyWhitelist.Contains(type.Key))
+ return false;
+ }
+
+ foreach (var type in currency)
+ {
+ if (!store.Balance.TryAdd(type.Key, type.Value))
+ store.Balance[type.Key] += type.Value;
+ }
+
+ UpdateUserInterface(null, store);
+ return true;
+ }
+
+ ///
+ /// Initializes a store based on a preset ID
+ ///
+ /// The ID of a store preset prototype
+ /// The store being initialized
+ public void InitializeFromPreset(string? preset, StoreComponent component)
+ {
+ if (preset == null)
+ return;
+
+ if (!_proto.TryIndex(preset, out var proto))
+ return;
+
+ InitializeFromPreset(proto, component);
+ }
+
+ ///
+ /// Initializes a store based on a given preset
+ ///
+ /// The StorePresetPrototype
+ /// The store being initialized
+ public void InitializeFromPreset(StorePresetPrototype preset, StoreComponent component)
+ {
+ RefreshAllListings(component);
+ component.Preset = preset.ID;
+ component.CurrencyWhitelist.UnionWith(preset.CurrencyWhitelist);
+ component.Categories.UnionWith(preset.Categories);
+ if (component.Balance == new Dictionary() && preset.InitialBalance != null) //if we don't have a value stored, use the preset
+ TryAddCurrency(preset.InitialBalance, component);
+
+ var ui = component.Owner.GetUIOrNull(StoreUiKey.Key);
+ ui?.SetState(new StoreInitializeState(preset.StoreName));
+ }
+}
diff --git a/Content.Server/Traitor/Uplink/Account/UplinkAccountEvents.cs b/Content.Server/Traitor/Uplink/Account/UplinkAccountEvents.cs
deleted file mode 100644
index 0345df2fac..0000000000
--- a/Content.Server/Traitor/Uplink/Account/UplinkAccountEvents.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Content.Shared.Traitor.Uplink;
-
-namespace Content.Server.Traitor.Uplink.Account
-{
- ///
- /// Invokes when one of the UplinkAccounts changed its TC balance
- ///
- public sealed class UplinkAccountBalanceChanged : EntityEventArgs
- {
- public readonly UplinkAccount Account;
-
- ///
- /// Difference between NewBalance - OldBalance
- ///
- public readonly int Difference;
-
- public readonly int NewBalance;
- public readonly int OldBalance;
-
- public UplinkAccountBalanceChanged(UplinkAccount account, int difference)
- {
- Account = account;
- Difference = difference;
-
- NewBalance = account.Balance;
- OldBalance = account.Balance - difference;
-
- }
- }
-}
diff --git a/Content.Server/Traitor/Uplink/Account/UplinkAccountsSystem.cs b/Content.Server/Traitor/Uplink/Account/UplinkAccountsSystem.cs
deleted file mode 100644
index d3ad87dd25..0000000000
--- a/Content.Server/Traitor/Uplink/Account/UplinkAccountsSystem.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using Content.Shared.Stacks;
-using Content.Shared.Traitor.Uplink;
-using Robust.Shared.Map;
-
-namespace Content.Server.Traitor.Uplink.Account
-{
- ///
- /// Manage all registred uplink accounts and their balance
- ///
- public sealed class UplinkAccountsSystem : EntitySystem
- {
- public const string TelecrystalProtoId = "Telecrystal";
-
- [Dependency]
- private readonly UplinkListingSytem _listingSystem = default!;
- [Dependency]
- private readonly SharedStackSystem _stackSystem = default!;
-
- private readonly HashSet _accounts = new();
-
- public bool AddNewAccount(UplinkAccount acc)
- {
- return _accounts.Add(acc);
- }
-
- public bool HasAccount(EntityUid holder) =>
- _accounts.Any(acct => acct.AccountHolder == holder);
-
- ///
- /// Add TC to uplinks account balance
- ///
- public bool AddToBalance(UplinkAccount account, int toAdd)
- {
- account.Balance += toAdd;
-
- RaiseLocalEvent(new UplinkAccountBalanceChanged(account, toAdd));
- return true;
- }
-
- ///
- /// Charge TC from uplinks account balance
- ///
- public bool RemoveFromBalance(UplinkAccount account, int price)
- {
- if (account.Balance - price < 0)
- return false;
-
- account.Balance -= price;
-
- RaiseLocalEvent(new UplinkAccountBalanceChanged(account, -price));
- return true;
- }
-
- ///
- /// Force-set TC uplinks account balance to a new value
- ///
- public bool SetBalance(UplinkAccount account, int newBalance)
- {
- if (newBalance < 0)
- return false;
-
- var dif = newBalance - account.Balance;
- account.Balance = newBalance;
- RaiseLocalEvent(new UplinkAccountBalanceChanged(account, dif));
- return true;
-
- }
-
- public bool TryPurchaseItem(UplinkAccount acc, string itemId, EntityCoordinates spawnCoords, [NotNullWhen(true)] out EntityUid? purchasedItem)
- {
- purchasedItem = null;
-
- if (!_listingSystem.TryGetListing(itemId, out var listing))
- return false;
-
- if (acc.Balance < listing.Price)
- return false;
-
- if (!RemoveFromBalance(acc, listing.Price))
- return false;
-
- purchasedItem = EntityManager.SpawnEntity(listing.ItemId, spawnCoords);
- return true;
- }
-
- public bool TryWithdrawTC(UplinkAccount acc, int tc, EntityCoordinates spawnCoords, [NotNullWhen(true)] out EntityUid? stackUid)
- {
- stackUid = null;
-
- // try to charge TC from players account
- var actTC = Math.Min(tc, acc.Balance);
- if (actTC <= 0)
- return false;
- if (!RemoveFromBalance(acc, actTC))
- return false;
-
- // create a stack of TCs near player
- var stackEntity = EntityManager.SpawnEntity(TelecrystalProtoId, spawnCoords);
- stackUid = stackEntity;
-
- // set right amount in stack
- _stackSystem.SetCount(stackUid.Value, actTC);
- return true;
- }
- }
-}
diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs
index 156a13e465..5145c2d99d 100644
--- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs
+++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs
@@ -1,8 +1,7 @@
using Content.Server.Administration;
-using Content.Server.Traitor.Uplink.Account;
using Content.Shared.Administration;
using Content.Shared.CCVar;
-using Content.Shared.Traitor.Uplink;
+using Content.Shared.FixedPoint;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
@@ -82,15 +81,10 @@ namespace Content.Server.Traitor.Uplink.Commands
// Get TC count
var configManager = IoCManager.Resolve();
var tcCount = configManager.GetCVar(CCVars.TraitorStartingBalance);
-
- // Get account
- var uplinkAccount = new UplinkAccount(tcCount, user);
- var accounts = entityManager.EntitySysManager.GetEntitySystem();
- accounts.AddNewAccount(uplinkAccount);
-
+ Logger.Debug(entityManager.ToPrettyString(user));
// Finally add uplink
- if (!entityManager.EntitySysManager.GetEntitySystem()
- .AddUplink(user, uplinkAccount, uplinkEntity))
+ var uplinkSys = entityManager.EntitySysManager.GetEntitySystem();
+ if (!uplinkSys.AddUplink(user, FixedPoint2.New(tcCount), uplinkEntity: uplinkEntity))
{
shell.WriteLine(Loc.GetString("add-uplink-command-error-2"));
return;
diff --git a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs
index 5682863f2f..8258034e67 100644
--- a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs
+++ b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleComponent.cs
@@ -1,3 +1,6 @@
+using Content.Shared.Store;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
namespace Content.Server.Traitor.Uplink.SurplusBundle;
///
@@ -12,4 +15,11 @@ public sealed class SurplusBundleComponent : Component
[ViewVariables(VVAccess.ReadOnly)]
[DataField("totalPrice")]
public int TotalPrice = 20;
+
+ ///
+ /// The preset that will be used to get all the listings.
+ /// Currently just defaults to the basic uplink.
+ ///
+ [DataField("storePreset", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string StorePreset = "StorePresetUplink";
}
diff --git a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs
index fa704aef22..11187e969f 100644
--- a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs
+++ b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs
@@ -1,7 +1,8 @@
using System.Linq;
-using Content.Server.Storage.Components;
+using Content.Server.Store.Systems;
using Content.Server.Storage.EntitySystems;
-using Content.Shared.PDA;
+using Content.Shared.Store;
+using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -12,23 +13,25 @@ public sealed class SurplusBundleSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
+ [Dependency] private readonly StoreSystem _store = default!;
- private UplinkStoreListingPrototype[] _uplinks = default!;
+ private ListingData[] _listings = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnMapInit);
- InitList();
+ SubscribeLocalEvent(OnInit);
}
- private void InitList()
+ private void OnInit(EntityUid uid, SurplusBundleComponent component, ComponentInit args)
{
- // sort data in price descending order
- _uplinks = _prototypeManager.EnumeratePrototypes()
- .Where(item => item.CanSurplus).ToArray();
- Array.Sort(_uplinks, (a, b) => b.Price - a.Price);
+ var storePreset = _prototypeManager.Index(component.StorePreset);
+
+ _listings = _store.GetAvailableListings(uid, null, storePreset.Categories).ToArray();
+
+ Array.Sort(_listings, (a, b) => (int) (b.Cost.Values.Sum() - a.Cost.Values.Sum())); //this might get weird with multicurrency but don't think about it
}
private void OnMapInit(EntityUid uid, SurplusBundleComponent component, MapInitEvent args)
@@ -46,19 +49,19 @@ public sealed class SurplusBundleSystem : EntitySystem
var content = GetRandomContent(component.TotalPrice);
foreach (var item in content)
{
- var ent = EntityManager.SpawnEntity(item.ItemId, cords);
+ var ent = EntityManager.SpawnEntity(item.ProductEntity, cords);
_entityStorage.Insert(ent, component.Owner);
}
}
// wow, is this leetcode reference?
- private List GetRandomContent(int targetCost)
+ private List GetRandomContent(FixedPoint2 targetCost)
{
- var ret = new List();
- if (_uplinks.Length == 0)
+ var ret = new List();
+ if (_listings.Length == 0)
return ret;
- var totalCost = 0;
+ var totalCost = FixedPoint2.Zero;
var index = 0;
while (totalCost < targetCost)
{
@@ -66,10 +69,10 @@ public sealed class SurplusBundleSystem : EntitySystem
// Find new item with the lowest acceptable price
// All expansive items will be before index, all acceptable after
var remainingBudget = targetCost - totalCost;
- while (_uplinks[index].Price > remainingBudget)
+ while (_listings[index].Cost.Values.Sum() > remainingBudget)
{
index++;
- if (index >= _uplinks.Length)
+ if (index >= _listings.Length)
{
// Looks like no cheap items left
// It shouldn't be case for ss14 content
@@ -79,10 +82,10 @@ public sealed class SurplusBundleSystem : EntitySystem
}
// Select random listing and add into crate
- var randomIndex = _random.Next(index, _uplinks.Length);
- var randomItem = _uplinks[randomIndex];
+ var randomIndex = _random.Next(index, _listings.Length);
+ var randomItem = _listings[randomIndex];
ret.Add(randomItem);
- totalCost += randomItem.Price;
+ totalCost += randomItem.Cost.Values.Sum();
}
return ret;
diff --git a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalComponent.cs b/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalComponent.cs
deleted file mode 100644
index 9b3a3636b8..0000000000
--- a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalComponent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Content.Server.Traitor.Uplink.Telecrystal
-{
- [RegisterComponent]
- public sealed class TelecrystalComponent : Component
- {
- }
-}
diff --git a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs b/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs
deleted file mode 100644
index cf98452338..0000000000
--- a/Content.Server/Traitor/Uplink/Telecrystal/TelecrystalSystem.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Content.Server.Traitor.Uplink.Account;
-using Content.Server.Traitor.Uplink.Components;
-using Content.Shared.Interaction;
-using Content.Shared.Popups;
-using Content.Shared.Stacks;
-
-namespace Content.Server.Traitor.Uplink.Telecrystal
-{
- public sealed class TelecrystalSystem : EntitySystem
- {
- [Dependency]
- private readonly UplinkAccountsSystem _accounts = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnAfterInteract);
- }
-
- private void OnAfterInteract(EntityUid uid, TelecrystalComponent component, AfterInteractEvent args)
- {
- if (args.Handled || !args.CanReach)
- return;
-
- if (args.Target == null || !EntityManager.TryGetComponent(args.Target.Value, out UplinkComponent? uplink))
- return;
-
- // TODO: when uplink will have some auth logic (like PDA ringtone code)
- // check if uplink open before adding TC
- // No metagaming by using this on every PDA around just to see if it gets used up.
-
- var acc = uplink.UplinkAccount;
- if (acc == null)
- return;
-
- EntityManager.TryGetComponent(uid, out SharedStackComponent? stack);
-
- var tcCount = stack != null ? stack.Count : 1;
- if (!_accounts.AddToBalance(acc, tcCount))
- return;
-
- var msg = Loc.GetString("telecrystal-component-sucs-inserted",
- ("source", args.Used), ("target", args.Target));
-
- args.User.PopupMessage(args.User, msg);
-
- EntityManager.DeleteEntity(uid);
-
- args.Handled = true;
- }
- }
-}
diff --git a/Content.Server/Traitor/Uplink/UplinkComponent.cs b/Content.Server/Traitor/Uplink/UplinkComponent.cs
deleted file mode 100644
index efa1afd925..0000000000
--- a/Content.Server/Traitor/Uplink/UplinkComponent.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Content.Shared.Roles;
-using Content.Shared.Traitor.Uplink;
-using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-
-namespace Content.Server.Traitor.Uplink.Components
-{
- [RegisterComponent]
- public sealed class UplinkComponent : Component
- {
- [ViewVariables]
- [DataField("buySuccessSound")]
- public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg");
-
- [ViewVariables]
- [DataField("insufficientFundsSound")]
- public SoundSpecifier InsufficientFundsSound = new SoundPathSpecifier("/Audio/Effects/error.ogg");
-
- [DataField("activatesInHands")]
- public bool ActivatesInHands = false;
-
- [DataField("presetInfo")]
- public PresetUplinkInfo? PresetInfo = null;
-
- [ViewVariables] public UplinkAccount? UplinkAccount;
-
- [ViewVariables, DataField("jobWhiteList", customTypeSerializer:typeof(PrototypeIdHashSetSerializer))]
- public HashSet? JobWhitelist = null;
-
- [Serializable]
- [DataDefinition]
- public sealed class PresetUplinkInfo
- {
- [DataField("balance")]
- public int StartingBalance;
- }
- }
-}
diff --git a/Content.Server/Traitor/Uplink/UplinkEvents.cs b/Content.Server/Traitor/Uplink/UplinkEvents.cs
deleted file mode 100644
index 6de247a59a..0000000000
--- a/Content.Server/Traitor/Uplink/UplinkEvents.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Content.Server.Traitor.Uplink.Components;
-
-namespace Content.Server.Traitor.Uplink
-{
- public sealed class UplinkInitEvent : EntityEventArgs
- {
- public UplinkComponent Uplink;
-
- public UplinkInitEvent(UplinkComponent uplink)
- {
- Uplink = uplink;
- }
- }
-
- public sealed class UplinkRemovedEvent : EntityEventArgs
- {
- }
-}
diff --git a/Content.Server/Traitor/Uplink/UplinkListingSytem.cs b/Content.Server/Traitor/Uplink/UplinkListingSytem.cs
deleted file mode 100644
index 67458ad5f8..0000000000
--- a/Content.Server/Traitor/Uplink/UplinkListingSytem.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using Content.Shared.PDA;
-using Content.Shared.Traitor.Uplink;
-using Robust.Shared.Prototypes;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Content.Server.Traitor.Uplink
-{
- ///
- /// Contains and controls all items in traitors uplink shop
- ///
- public sealed class UplinkListingSytem : EntitySystem
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-
- private readonly Dictionary _listings = new();
-
- public override void Initialize()
- {
- base.Initialize();
-
- foreach (var item in _prototypeManager.EnumeratePrototypes())
- {
- var newListing = new UplinkListingData(item.ListingName, item.ItemId,
- item.Price, item.Category, item.Description, item.Icon, item.JobWhitelist);
-
- RegisterUplinkListing(newListing);
- }
- }
-
- private void RegisterUplinkListing(UplinkListingData listing)
- {
- if (!ContainsListing(listing))
- {
- _listings.Add(listing.ItemId, listing);
- }
- }
-
- public bool ContainsListing(UplinkListingData listing)
- {
- return _listings.ContainsKey(listing.ItemId);
- }
-
- public bool TryGetListing(string itemID, [NotNullWhen(true)] out UplinkListingData? data)
- {
- return _listings.TryGetValue(itemID, out data);
- }
-
- public IReadOnlyDictionary GetListings()
- {
- return _listings;
- }
- }
-}
diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs
index 6f8da4f627..0a5968aa7a 100644
--- a/Content.Server/Traitor/Uplink/UplinkSystem.cs
+++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs
@@ -1,230 +1,41 @@
-using System.Linq;
-using Content.Server.Mind.Components;
-using Content.Server.Roles;
-using Content.Server.Traitor.Uplink.Account;
-using Content.Server.Traitor.Uplink.Components;
-using Content.Server.UserInterface;
+using Content.Server.Store.Systems;
using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.PDA;
-using Content.Shared.Traitor.Uplink;
-using Robust.Server.GameObjects;
-using Robust.Server.Player;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
+using Content.Server.Store.Components;
+using Content.Shared.FixedPoint;
namespace Content.Server.Traitor.Uplink
{
public sealed class UplinkSystem : EntitySystem
{
- [Dependency]
- private readonly UplinkAccountsSystem _accounts = default!;
- [Dependency]
- private readonly UplinkListingSytem _listing = default!;
-
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly StoreSystem _store = default!;
- public override void Initialize()
+ public const string TelecrystalCurrencyPrototype = "Telecrystal";
+
+ ///
+ /// Gets the amount of TC on an "uplink"
+ /// Mostly just here for legacy systems based on uplink.
+ ///
+ ///
+ /// the amount of TC
+ public int GetTCBalance(StoreComponent component)
{
- base.Initialize();
-
- SubscribeLocalEvent(OnInit);
- SubscribeLocalEvent(OnRemove);
- SubscribeLocalEvent(OnActivate);
-
- // UI events
- SubscribeLocalEvent(OnBuy);
- SubscribeLocalEvent(OnRequestUpdateUI);
- SubscribeLocalEvent(OnWithdrawTC);
-
- SubscribeLocalEvent(OnBalanceChangedBroadcast);
+ FixedPoint2? tcBalance = component.Balance.GetValueOrDefault(TelecrystalCurrencyPrototype);
+ return tcBalance != null ? tcBalance.Value.Int() : 0;
}
- public void SetAccount(UplinkComponent component, UplinkAccount account)
- {
- if (component.UplinkAccount != null)
- {
- Logger.Error("Can't init one uplink with different account!");
- return;
- }
-
- component.UplinkAccount = account;
- }
-
- private void OnInit(EntityUid uid, UplinkComponent component, ComponentInit args)
- {
- RaiseLocalEvent(uid, new UplinkInitEvent(component), true);
-
- // if component has a preset info (probably spawn by admin)
- // create a new account and register it for this uplink
- if (component.PresetInfo != null)
- {
- var account = new UplinkAccount(component.PresetInfo.StartingBalance);
- _accounts.AddNewAccount(account);
- SetAccount(component, account);
- }
- }
-
- private void OnRemove(EntityUid uid, UplinkComponent component, ComponentRemove args)
- {
- RaiseLocalEvent(uid, new UplinkRemovedEvent(), true);
- }
-
- private void OnActivate(EntityUid uid, UplinkComponent component, ActivateInWorldEvent args)
- {
- if (args.Handled)
- return;
-
- // check if uplinks activates directly or use some proxy, like a PDA
- if (!component.ActivatesInHands)
- return;
- if (component.UplinkAccount == null)
- return;
-
- if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
- return;
-
- ToggleUplinkUI(component, actor.PlayerSession);
- args.Handled = true;
- }
-
- private void OnBalanceChangedBroadcast(UplinkAccountBalanceChanged ev)
- {
- foreach (var uplink in EntityManager.EntityQuery())
- {
- if (uplink.UplinkAccount == ev.Account)
- {
- UpdateUserInterface(uplink);
- }
- }
- }
-
- private void OnRequestUpdateUI(EntityUid uid, UplinkComponent uplink, UplinkRequestUpdateInterfaceMessage args)
- {
- UpdateUserInterface(uplink);
- }
-
- private void OnBuy(EntityUid uid, UplinkComponent uplink, UplinkBuyListingMessage message)
- {
- if (message.Session.AttachedEntity is not { Valid: true } player) return;
- if (uplink.UplinkAccount == null) return;
-
- if (!_accounts.TryPurchaseItem(uplink.UplinkAccount, message.ItemId,
- EntityManager.GetComponent(player).Coordinates, out var entity))
- {
- SoundSystem.Play(uplink.InsufficientFundsSound.GetSound(),
- Filter.SinglePlayer(message.Session), uplink.Owner, AudioParams.Default);
- RaiseNetworkEvent(new UplinkInsufficientFundsMessage(), message.Session.ConnectedClient);
- return;
- }
-
- _handsSystem.PickupOrDrop(player, entity.Value);
-
- SoundSystem.Play(uplink.BuySuccessSound.GetSound(),
- Filter.SinglePlayer(message.Session), uplink.Owner, AudioParams.Default.WithVolume(-8f));
-
- RaiseNetworkEvent(new UplinkBuySuccessMessage(), message.Session.ConnectedClient);
- }
-
- private void OnWithdrawTC(EntityUid uid, UplinkComponent uplink, UplinkTryWithdrawTC args)
- {
- var acc = uplink.UplinkAccount;
- if (acc == null)
- return;
-
- if (args.Session.AttachedEntity is not { Valid: true } player) return;
- var cords = EntityManager.GetComponent(player).Coordinates;
-
- // try to withdraw TCs from account
- if (!_accounts.TryWithdrawTC(acc, args.TC, cords, out var tcUid))
- return;
-
- // try to put it into players hands
- _handsSystem.PickupOrDrop(player, tcUid.Value);
-
- // play buying sound
- SoundSystem.Play(uplink.BuySuccessSound.GetSound(),
- Filter.SinglePlayer(args.Session), uplink.Owner, AudioParams.Default.WithVolume(-8f));
-
- UpdateUserInterface(uplink);
- }
-
- public void ToggleUplinkUI(UplinkComponent component, IPlayerSession session)
- {
- var ui = component.Owner.GetUIOrNull(UplinkUiKey.Key);
- ui?.Toggle(session);
-
- UpdateUserInterface(component);
- }
-
- private void UpdateUserInterface(UplinkComponent component)
- {
- var ui = component.Owner.GetUIOrNull(UplinkUiKey.Key);
- if (ui == null)
- return;
-
- var listings = _listing.GetListings().Values.ToList();
- var acc = component.UplinkAccount;
-
- UplinkAccountData accData;
- if (acc != null)
- {
- // if we don't have a jobwhitelist stored, get a new one
- if (component.JobWhitelist == null &&
- acc.AccountHolder != null &&
- TryComp(acc.AccountHolder, out var mind) &&
- mind.Mind != null)
- {
- HashSet? jobList = new();
- foreach (var role in mind.Mind.AllRoles.ToList())
- {
- if (role.GetType() == typeof(Job))
- {
- var job = (Job) role;
- jobList.Add(job.Prototype.ID);
- }
- }
- component.JobWhitelist = jobList;
- }
-
- // filter out items not on the whitelist
- for (var i = 0; i < listings.Count; i++)
- {
- var entry = listings[i];
- if (entry.JobWhitelist != null)
- {
- var found = false;
- if (component.JobWhitelist != null)
- {
- foreach (var job in component.JobWhitelist)
- {
- if (entry.JobWhitelist.Contains(job))
- {
- found = true;
- break;
- }
- }
- }
- if (!found)
- {
- listings.Remove(entry);
- i--;
- }
- }
- }
- accData = new UplinkAccountData(acc.AccountHolder, acc.Balance);
- }
- else
- {
- accData = new UplinkAccountData(null, 0);
- }
-
- ui.SetState(new UplinkUpdateState(accData, listings.ToArray()));
- }
-
- public bool AddUplink(EntityUid user, UplinkAccount account, EntityUid? uplinkEntity = null)
+ ///
+ /// Adds an uplink to the target
+ ///
+ /// The person who is getting the uplink
+ /// The amount of currency on the uplink. If null, will just use the amount specified in the preset.
+ /// The id of the storepreset
+ /// The entity that will actually have the uplink functionality. Defaults to the PDA if null.
+ /// Whether or not the uplink was added successfully
+ public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetId = "StorePresetUplink", EntityUid? uplinkEntity = null)
{
// Try to find target item
if (uplinkEntity == null)
@@ -234,11 +45,17 @@ namespace Content.Server.Traitor.Uplink
return false;
}
- var uplink = uplinkEntity.Value.EnsureComponent();
- SetAccount(uplink, account);
+ var store = EnsureComp(uplinkEntity.Value);
+ _store.InitializeFromPreset(uplinkPresetId, store);
+ store.AccountOwner = user;
+ store.Balance.Clear();
- if (!HasComp(uplinkEntity.Value))
- uplink.ActivatesInHands = true;
+ if (balance != null)
+ {
+ store.Balance.Clear();
+ _store.TryAddCurrency(
+ new Dictionary() { { TelecrystalCurrencyPrototype, balance.Value } }, store);
+ }
// TODO add BUI. Currently can't be done outside of yaml -_-
@@ -248,14 +65,13 @@ namespace Content.Server.Traitor.Uplink
private EntityUid? FindUplinkTarget(EntityUid user)
{
// Try to find PDA in inventory
-
if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator))
{
while (containerSlotEnumerator.MoveNext(out var pdaUid))
{
if (!pdaUid.ContainedEntity.HasValue) continue;
- if (HasComp(pdaUid.ContainedEntity.Value))
+ if (HasComp(pdaUid.ContainedEntity.Value) || HasComp(pdaUid.ContainedEntity.Value))
return pdaUid.ContainedEntity.Value;
}
}
@@ -263,7 +79,7 @@ namespace Content.Server.Traitor.Uplink
// Also check hands
foreach (var item in _handsSystem.EnumerateHeld(user))
{
- if (HasComp(item))
+ if (HasComp(item) || HasComp(item))
return item;
}
diff --git a/Content.Server/TraitorDeathMatch/TraitorDeathMatchRedemptionSystem.cs b/Content.Server/TraitorDeathMatch/TraitorDeathMatchRedemptionSystem.cs
index 3eb7912590..1c5908fe14 100644
--- a/Content.Server/TraitorDeathMatch/TraitorDeathMatchRedemptionSystem.cs
+++ b/Content.Server/TraitorDeathMatch/TraitorDeathMatchRedemptionSystem.cs
@@ -1,7 +1,9 @@
-using Content.Server.Mind.Components;
-using Content.Server.Traitor.Uplink.Account;
-using Content.Server.Traitor.Uplink.Components;
+using Content.Server.Mind.Components;
using Content.Server.TraitorDeathMatch.Components;
+using Content.Server.Store.Components;
+using Content.Server.Store.Systems;
+using Content.Server.Traitor.Uplink;
+using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Popups;
@@ -12,8 +14,11 @@ namespace Content.Server.TraitorDeathMatch;
public sealed class TraitorDeathMatchRedemptionSystem : EntitySystem
{
[Dependency] private readonly InventorySystem _inventory = default!;
- [Dependency] private readonly UplinkAccountsSystem _uplink = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly UplinkSystem _uplink = default!;
+ [Dependency] private readonly StoreSystem _store = default!;
+
+ private const string TcCurrencyPrototype = "Telecrystal";
public override void Initialize()
{
@@ -43,7 +48,7 @@ public sealed class TraitorDeathMatchRedemptionSystem : EntitySystem
return;
}
- if (!EntityManager.TryGetComponent