diff --git a/Content.Server/Storage/Components/ServerStorageComponent.cs b/Content.Server/Storage/Components/ServerStorageComponent.cs
index 3a557b4e51..8c5014504f 100644
--- a/Content.Server/Storage/Components/ServerStorageComponent.cs
+++ b/Content.Server/Storage/Components/ServerStorageComponent.cs
@@ -71,6 +71,15 @@ namespace Content.Server.Storage.Components
[DataField("whitelist")]
private EntityWhitelist? _whitelist = null;
+ [DataField("blacklist")]
+ public EntityWhitelist? Blacklist = null;
+
+ ///
+ /// If true, storage will show popup messages to the player after failed interactions.
+ /// Usually this is message that item doesn't fit inside container.
+ ///
+ [DataField("popup")]
+ public bool ShowPopup = true;
private bool _storageInitialCalculated;
public int StorageUsed;
@@ -165,6 +174,11 @@ namespace Content.Server.Storage.Components
return false;
}
+ if (Blacklist != null && Blacklist.IsValid(entity))
+ {
+ return false;
+ }
+
if (_entityManager.GetComponent(entity).Anchored)
{
return false;
@@ -256,14 +270,14 @@ namespace Content.Server.Storage.Components
if (!handSys.TryDrop(player, toInsert.Value, handsComp: hands))
{
- Owner.PopupMessage(player, Loc.GetString("comp-storage-cant-insert"));
+ Popup(player, "comp-storage-cant-insert");
return false;
}
if (!Insert(toInsert.Value))
{
handSys.PickupOrDrop(player, toInsert.Value, handsComp: hands);
- Owner.PopupMessage(player, Loc.GetString("comp-storage-cant-insert"));
+ Popup(player, "comp-storage-cant-insert");
return false;
}
@@ -282,7 +296,7 @@ namespace Content.Server.Storage.Components
if (!Insert(toInsert))
{
- Owner.PopupMessage(player, Loc.GetString("comp-storage-cant-insert"));
+ Popup(player, "comp-storage-cant-insert");
return false;
}
return true;
@@ -482,7 +496,7 @@ namespace Content.Server.Storage.Components
return;
}
- if (!EntitySystem.Get().InRangeUnobstructed(player, Owner, popup: true))
+ if (!EntitySystem.Get().InRangeUnobstructed(player, Owner, popup: ShowPopup))
{
return;
}
@@ -638,6 +652,13 @@ namespace Content.Server.Storage.Components
}
}
+ private void Popup(EntityUid player, string message)
+ {
+ if (!ShowPopup) return;
+
+ Owner.PopupMessage(player, Loc.GetString(message));
+ }
+
private void PlaySoundCollection()
{
SoundSystem.Play(Filter.Pvs(Owner), StorageSoundCollection.GetSound(), Owner, AudioParams.Default);
diff --git a/Content.Server/Storage/EntitySystems/ItemMapperSystem.cs b/Content.Server/Storage/EntitySystems/ItemMapperSystem.cs
index 46d17fca1b..12344a1e25 100644
--- a/Content.Server/Storage/EntitySystems/ItemMapperSystem.cs
+++ b/Content.Server/Storage/EntitySystems/ItemMapperSystem.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Linq;
using Content.Server.Storage.Components;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
@@ -12,32 +13,27 @@ namespace Content.Server.Storage.EntitySystems
[UsedImplicitly]
public sealed class ItemMapperSystem : SharedItemMapperSystem
{
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+
protected override bool TryGetLayers(ContainerModifiedMessage msg,
ItemMapperComponent itemMapper,
out IReadOnlyList showLayers)
{
- if (EntityManager.TryGetComponent(msg.Container.Owner, out ServerStorageComponent? component))
- {
- var containedLayers = component.StoredEntities ?? new List();
- var list = new List();
- foreach (var mapLayerData in itemMapper.MapLayers.Values)
- {
- foreach (var entity in containedLayers)
- {
- if (mapLayerData.ServerWhitelist.IsValid(entity))
- {
- list.Add(mapLayerData.Layer);
- break;
- }
- }
- }
+ var containedLayers = _containerSystem.GetAllContainers(msg.Container.Owner)
+ .SelectMany(cont => cont.ContainedEntities).ToArray();
- showLayers = list;
- return true;
+ var list = new List();
+ foreach (var mapLayerData in itemMapper.MapLayers.Values)
+ {
+ var count = containedLayers.Count(uid => mapLayerData.ServerWhitelist.IsValid(uid));
+ if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount)
+ {
+ list.Add(mapLayerData.Layer);
+ }
}
- showLayers = new List();
- return false;
+ showLayers = list;
+ return true;
}
}
}
diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs
index 0c193be5f7..ebe5971dd3 100644
--- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs
+++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs
@@ -116,6 +116,13 @@ namespace Content.Shared.Containers.ItemSlots
[ViewVariables(VVAccess.ReadWrite)]
public bool Locked = false;
+ ///
+ /// Whether the item slots system will attempt to insert item from the user's hands into this slot when interacted with.
+ /// It doesn't block other insertion methods, like verbs.
+ ///
+ [DataField("insertOnInteract")]
+ public bool InsertOnInteract = true;
+
///
/// Whether the item slots system will attempt to eject this item to the user's hands when interacted with.
///
diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs
index 65c051dff4..bf461be347 100644
--- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs
+++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs
@@ -170,6 +170,9 @@ namespace Content.Shared.Containers.ItemSlots
foreach (var slot in itemSlots.Slots.Values)
{
+ if (!slot.InsertOnInteract)
+ continue;
+
if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User))
continue;
diff --git a/Content.Shared/Storage/Components/SharedMapLayerData.cs b/Content.Shared/Storage/Components/SharedMapLayerData.cs
index 048e0b02ce..604b106cb3 100644
--- a/Content.Shared/Storage/Components/SharedMapLayerData.cs
+++ b/Content.Shared/Storage/Components/SharedMapLayerData.cs
@@ -21,6 +21,20 @@ namespace Content.Shared.Storage.Components
[DataField("whitelist", required: true, serverOnly: true)]
public EntityWhitelist ServerWhitelist { get; set; } = new();
+
+ ///
+ /// Minimal amount of entities that are valid for whitelist.
+ /// If it's smaller than minimal amount, layer will be hidden.
+ ///
+ [DataField("minCount")]
+ public int MinCount = 1;
+
+ ///
+ /// Max amount of entities that are valid for whitelist.
+ /// If it's bigger than max amount, layer will be hidden.
+ ///
+ [DataField("maxCount")]
+ public int MaxCount = int.MaxValue;
}
[Serializable, NetSerializable]
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml
index a31456d265..0666af8233 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml
@@ -9,7 +9,7 @@
sprite: Objects/Specific/Janitorial/mop.rsi
state: mop
- type: Item
- size: 10
+ size: 15
sprite: Objects/Specific/Janitorial/mop.rsi
- type: Absorbent
- type: SolutionContainerManager
@@ -74,3 +74,129 @@
state: caution
- type: Item
sprite: Objects/Specific/Janitorial/wet_floor_sign.rsi
+ size: 15
+ - type: Tag
+ tags:
+ - WetFloorSign
+
+- type: entity
+ name: janitorial trolley
+ id: JanitorialTrolley
+ parent: BaseStructureDynamic
+ description: This is the alpha and omega of sanitation.
+ components:
+ - type: Sprite
+ netSync: false
+ noRot: true
+ sprite: Objects/Specific/Janitorial/janitorial_cart.rsi
+ layers:
+ - state: cart
+ - state: cart_water_1
+ map: ["enum.SolutionContainerLayers.Fill"]
+ - type: Rotatable
+ - type: InteractionOutline
+ - type: Storage
+ popup: false
+ capacity: 80
+ blacklist: # there is exclusive item slots for that
+ tags:
+ - Mop
+ - TrashBag
+ - type: ItemSlots
+ slots:
+ mop_slot:
+ name: Mop
+ whitelist:
+ tags:
+ - Mop
+ insertOnInteract: false # or it conflicts with bucket logic
+ trashbag_slot:
+ name: Bag
+ whitelist:
+ tags:
+ - TrashBag
+ - type: Fixtures
+ fixtures:
+ - shape:
+ !type:PhysShapeCircle
+ radius: 0.3
+ layer:
+ - SmallImpassable
+ mask:
+ - VaultImpassable
+ mass: 100
+ - type: SolutionContainerManager
+ solutions:
+ bucket:
+ maxVol: 500
+ reagents:
+ - ReagentId: Water
+ Quantity: 250 # half-full at roundstart to leave room for puddles
+ - type: DrainableSolution
+ solution: bucket
+ - type: RefillableSolution
+ solution: bucket
+ - type: Tag
+ tags:
+ - Wringer
+ - type: Damageable
+ damageContainer: Inorganic
+ damageModifierSet: Metallic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 200
+ behaviors:
+ - !type:EmptyAllContainersBehaviour
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - !type:PlaySoundBehavior
+ sound:
+ path: /Audio/Effects/metalbreak.ogg
+ - type: ItemMapper
+ mapLayers:
+ cart_mop:
+ whitelist:
+ tags:
+ - Mop
+ cart_garbage:
+ whitelist:
+ tags:
+ - TrashBag
+ cart_replacer:
+ whitelist:
+ components:
+ - LightReplacer
+ cart_spray:
+ whitelist:
+ tags:
+ - Spray
+ cart_sign1: # this is like stack of floor signs
+ minCount: 1
+ whitelist:
+ tags:
+ - WetFloorSign
+ cart_sign2:
+ minCount: 2
+ whitelist:
+ tags:
+ - WetFloorSign
+ cart_sign3:
+ minCount: 3
+ whitelist:
+ tags:
+ - WetFloorSign
+ cart_sign4:
+ minCount: 4
+ whitelist:
+ tags:
+ - WetFloorSign
+ - type: Appearance
+ visuals:
+ - type: MappedItemVisualizer
+ sprite: Objects/Specific/Janitorial/janitorial_cart.rsi
+ - type: SolutionContainerVisualizer
+ maxFillLevels: 3
+ fillBaseName: cart_water_
+ changeColor: false
diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml
index faa02344d3..75c37ff665 100644
--- a/Resources/Prototypes/tags.yml
+++ b/Resources/Prototypes/tags.yml
@@ -298,6 +298,9 @@
- type: Tag
id: TrashBag
+- type: Tag
+ id: WetFloorSign
+
- type: Tag
id: Wall
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart.png
new file mode 100644
index 0000000000..240bb4a061
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage.png
new file mode 100644
index 0000000000..bc8abf100f
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_mop.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_mop.png
new file mode 100644
index 0000000000..4e08a5e388
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_mop.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_replacer.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_replacer.png
new file mode 100644
index 0000000000..c0b0d6c397
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_replacer.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign1.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign1.png
new file mode 100644
index 0000000000..0af6aa459d
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign1.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign2.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign2.png
new file mode 100644
index 0000000000..0eb81e760f
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign2.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign3.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign3.png
new file mode 100644
index 0000000000..be6dbff973
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign3.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign4.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign4.png
new file mode 100644
index 0000000000..5c749d4b0e
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_sign4.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_spray.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_spray.png
new file mode 100644
index 0000000000..62af7e9836
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_spray.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_1.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_1.png
new file mode 100644
index 0000000000..371b6ccfb5
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_1.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_2.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_2.png
new file mode 100644
index 0000000000..153808e017
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_2.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_3.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_3.png
new file mode 100644
index 0000000000..20298c57b0
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_water_3.png differ
diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json
new file mode 100644
index 0000000000..5d2fd5515d
--- /dev/null
+++ b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json
@@ -0,0 +1,125 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428",
+ "states": [
+ {
+ "name": "cart",
+ "directions": 4
+ },
+ {
+ "name": "cart_garbage",
+ "directions": 4
+ },
+ {
+ "name": "cart_mop",
+ "directions": 4
+ },
+ {
+ "name": "cart_replacer",
+ "directions": 4
+ },
+ {
+ "name": "cart_sign1",
+ "directions": 4
+ },
+ {
+ "name": "cart_sign2",
+ "directions": 4
+ },
+ {
+ "name": "cart_sign3",
+ "directions": 4
+ },
+ {
+ "name": "cart_sign4",
+ "directions": 4
+ },
+ {
+ "name": "cart_spray",
+ "directions": 4
+ },
+ {
+ "name": "cart_water_1",
+ "directions": 4,
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "cart_water_2",
+ "directions": 4,
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "cart_water_3",
+ "directions": 4,
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file