diff --git a/Content.Server/Drone/Components/DroneComponent.cs b/Content.Server/Drone/Components/DroneComponent.cs index c3a4b6780c..94525cc332 100644 --- a/Content.Server/Drone/Components/DroneComponent.cs +++ b/Content.Server/Drone/Components/DroneComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Storage; +using Content.Shared.Storage; namespace Content.Server.Drone.Components { diff --git a/Content.Server/Drone/DroneSystem.cs b/Content.Server/Drone/DroneSystem.cs index 85668b845a..69b8fb7ac8 100644 --- a/Content.Server/Drone/DroneSystem.cs +++ b/Content.Server/Drone/DroneSystem.cs @@ -20,6 +20,8 @@ using Content.Server.Hands.Components; using Content.Server.UserInterface; using Robust.Shared.Player; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Storage; +using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Server.Drone @@ -32,6 +34,7 @@ namespace Content.Server.Drone [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; public override void Initialize() { @@ -120,9 +123,10 @@ namespace Content.Server.Drone if (TryComp(uid, out var hands) && hands.Count >= drone.Tools.Count) { - foreach (var entry in drone.Tools) + var items = EntitySpawnCollection.GetSpawns(drone.Tools, _robustRandom); + foreach (var entry in items) { - var item = EntityManager.SpawnEntity(entry.PrototypeId, spawnCoord); + var item = EntityManager.SpawnEntity(entry, spawnCoord); AddComp(item); if (!_handsSystem.TryPickupAnyHand(uid, item, checkActionBlocker: false)) { diff --git a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs index 7e0d13fd60..a1972a66d2 100644 --- a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs @@ -15,13 +15,12 @@ namespace Content.Server.Kitchen.Components [RegisterComponent, Friend(typeof(KitchenSpikeSystem))] public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent, ISuicideAct { - public int MeatParts; - public string? MeatPrototype; + public List? PrototypesToSpawn; // TODO: Spiking alive mobs? (Replace with uid) (deal damage to their limbs on spiking, kill on first butcher attempt?) public string MeatSource1p = "?"; public string MeatSource0 = "?"; - public string MeatName = "?"; + public string Victim = "?"; // Prevents simultaneous spiking of two bodies (could be replaced with CancellationToken, but I don't see any situation where Cancel could be called) public bool InUse; diff --git a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs index a67566531c..99e37e2f6f 100644 --- a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs @@ -12,6 +12,8 @@ using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Player; using System; +using Content.Shared.Storage; +using Robust.Shared.Random; using static Content.Shared.Kitchen.Components.SharedKitchenSpikeComponent; namespace Content.Server.Kitchen.EntitySystems @@ -20,6 +22,7 @@ namespace Content.Server.Kitchen.EntitySystems { [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -64,14 +67,14 @@ namespace Content.Server.Kitchen.EntitySystems if (Spikeable(uid, args.User, args.Dragged, component)) TrySpike(uid, args.User, args.Dragged, component); - + } private void OnInteractHand(EntityUid uid, KitchenSpikeComponent component, InteractHandEvent args) { if (args.Handled) return; - if (component.MeatParts > 0) { + if (component.PrototypesToSpawn?.Count > 0) { _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-knife-needed"), uid, Filter.Entities(args.User)); args.Handled = true; } @@ -92,13 +95,13 @@ namespace Content.Server.Kitchen.EntitySystems if (!Resolve(uid, ref component) || !Resolve(victimUid, ref butcherable)) return; - component.MeatPrototype = butcherable.SpawnedPrototype; - component.MeatParts = butcherable.Pieces; + // TODO VERY SUS + component.PrototypesToSpawn = EntitySpawnCollection.GetSpawns(butcherable.SpawnedEntities, _random); // This feels not okay, but entity is getting deleted on "Spike", for now... component.MeatSource1p = Loc.GetString("comp-kitchen-spike-remove-meat", ("victim", victimUid)); component.MeatSource0 = Loc.GetString("comp-kitchen-spike-remove-meat-last", ("victim", victimUid)); - component.MeatName = Loc.GetString("comp-kitchen-spike-meat-name", ("victim", victimUid)); + component.Victim = Name(victimUid); UpdateAppearance(uid, null, component); @@ -114,7 +117,7 @@ namespace Content.Server.Kitchen.EntitySystems private bool TryGetPiece(EntityUid uid, EntityUid user, EntityUid used, KitchenSpikeComponent? component = null, UtensilComponent? utensil = null) { - if (!Resolve(uid, ref component) || component.MeatParts == 0) + if (!Resolve(uid, ref component) || component.PrototypesToSpawn == null || component.PrototypesToSpawn.Count == 0) return false; // Is using knife @@ -123,15 +126,13 @@ namespace Content.Server.Kitchen.EntitySystems return false; } - component.MeatParts--; + var item = _random.PickAndTake(component.PrototypesToSpawn); - if (!string.IsNullOrEmpty(component.MeatPrototype)) - { - var meat = EntityManager.SpawnEntity(component.MeatPrototype, Transform(uid).Coordinates); - MetaData(meat).EntityName = component.MeatName; - } + var ent = Spawn(item, Transform(uid).Coordinates); + MetaData(ent).EntityName = + Loc.GetString("comp-kitchen-spike-meat-name", ("name", Name(ent)), ("victim", component.Victim)); - if (component.MeatParts != 0) + if (component.PrototypesToSpawn.Count != 0) { _popupSystem.PopupEntity(component.MeatSource1p, uid, Filter.Entities(user)); } @@ -149,7 +150,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!Resolve(uid, ref component, ref appearance, false)) return; - appearance.SetData(KitchenSpikeVisuals.Status, (component.MeatParts > 0) ? KitchenSpikeStatus.Bloody : KitchenSpikeStatus.Empty); + appearance.SetData(KitchenSpikeVisuals.Status, (component.PrototypesToSpawn?.Count > 0) ? KitchenSpikeStatus.Bloody : KitchenSpikeStatus.Empty); } private bool Spikeable(EntityUid uid, EntityUid userUid, EntityUid victimUid, @@ -158,13 +159,13 @@ namespace Content.Server.Kitchen.EntitySystems if (!Resolve(uid, ref component)) return false; - if (component.MeatParts > 0) + if (component.PrototypesToSpawn?.Count > 0) { _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-collect", ("this", uid)), uid, Filter.Entities(userUid)); return false; } - if (!Resolve(victimUid, ref butcherable, false) || butcherable.SpawnedPrototype == null) + if (!Resolve(victimUid, ref butcherable, false)) { _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", victimUid), ("this", uid)), victimUid, Filter.Entities(userUid)); return false; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index b8dac64971..814533590a 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -6,8 +6,10 @@ using Content.Shared.Interaction; using Content.Shared.MobState.Components; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; +using Content.Shared.Storage; using Content.Shared.Verbs; using Robust.Shared.Player; +using Robust.Shared.Random; namespace Content.Server.Kitchen.EntitySystems; @@ -15,6 +17,7 @@ public sealed class SharpSystem : EntitySystem { [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; public override void Initialize() { @@ -77,10 +80,12 @@ public sealed class SharpSystem : EntitySystem sharp.Butchering.Remove(ev.Entity); + var spawnEntities = EntitySpawnCollection.GetSpawns(butcher.SpawnedEntities, _robustRandom); + var coords = Transform(ev.Entity).Coordinates; EntityUid popupEnt = default; - for (int i = 0; i < butcher.Pieces; i++) + foreach (var proto in spawnEntities) { - popupEnt = Spawn(butcher.SpawnedPrototype, Transform(ev.Entity).Coordinates); + popupEnt = Spawn(proto, coords); } _popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", ev.Entity), ("knife", ev.Sharp)), @@ -118,7 +123,7 @@ public sealed class SharpSystem : EntitySystem message = Loc.GetString("butcherable-mob-isnt-dead"); } - if (args.Using is null || !TryComp(args.Using, out var sharp)) + if (args.Using is null || !HasComp(args.Using)) { disabled = true; message = Loc.GetString("butcherable-need-knife"); diff --git a/Content.Server/Storage/Components/SpawnItemsOnUseComponent.cs b/Content.Server/Storage/Components/SpawnItemsOnUseComponent.cs index 2fafd76d92..48f61c7eff 100644 --- a/Content.Server/Storage/Components/SpawnItemsOnUseComponent.cs +++ b/Content.Server/Storage/Components/SpawnItemsOnUseComponent.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Shared.Sound; +using Content.Shared.Storage; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -16,7 +17,7 @@ namespace Content.Server.Storage.Components /// /// [DataField("items", required: true)] - public List Items = new List(); + public List Items = new(); /// /// A sound to play when the items are spawned. For example, gift boxes being unwrapped. diff --git a/Content.Server/Storage/Components/StorageFillComponent.cs b/Content.Server/Storage/Components/StorageFillComponent.cs index 695b046641..1a449459bc 100644 --- a/Content.Server/Storage/Components/StorageFillComponent.cs +++ b/Content.Server/Storage/Components/StorageFillComponent.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Server.Storage.EntitySystems; +using Content.Shared.Storage; using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; diff --git a/Content.Server/Storage/EntitySpawnEntry.cs b/Content.Server/Storage/EntitySpawnEntry.cs deleted file mode 100644 index 99080980c0..0000000000 --- a/Content.Server/Storage/EntitySpawnEntry.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Storage -{ - /// - /// Dictates a list of items that can be spawned. - /// - [Serializable] - [DataDefinition] - public struct EntitySpawnEntry : IPopulateDefaultValues - { - [DataField("id", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PrototypeId; - - /// - /// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc. - /// - [DataField("prob")] - public float SpawnProbability; - - /// - /// orGroup signifies to pick between entities designated with an ID. - /// - /// - /// To define an orGroup in a StorageFill component you - /// need to add it to the entities you want to choose between and - /// add a prob field. In this example there is a 50% chance the storage - /// spawns with Y or Z. - /// - /// - /// - /// - type: StorageFill - /// contents: - /// - name: X - /// - name: Y - /// prob: 0.50 - /// orGroup: YOrZ - /// - name: Z - /// orGroup: YOrZ - /// - /// - /// - [DataField("orGroup")] - public string? GroupId; - - [DataField("amount")] - public int Amount; - - public void PopulateDefaultValues() - { - Amount = 1; - SpawnProbability = 1; - } - } -} diff --git a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs index 1b58b67b79..b9805abbb5 100644 --- a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs +++ b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Storage.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction.Events; +using Content.Shared.Storage; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Random; @@ -24,25 +25,13 @@ namespace Content.Server.Storage.EntitySystems if (args.Handled) return; - var alreadySpawnedGroups = new List(); + var coords = Transform(args.User).Coordinates; + var spawnEntities = EntitySpawnCollection.GetSpawns(component.Items, _random); EntityUid? entityToPlaceInHands = null; - foreach (var storageItem in component.Items) + + foreach (var proto in spawnEntities) { - if (!string.IsNullOrEmpty(storageItem.GroupId) && - alreadySpawnedGroups.Contains(storageItem.GroupId)) continue; - - if (storageItem.SpawnProbability != 1f && - !_random.Prob(storageItem.SpawnProbability)) - { - continue; - } - - for (var i = 0; i < storageItem.Amount; i++) - { - entityToPlaceInHands = EntityManager.SpawnEntity(storageItem.PrototypeId, EntityManager.GetComponent(args.User).Coordinates); - } - - if (!string.IsNullOrEmpty(storageItem.GroupId)) alreadySpawnedGroups.Add(storageItem.GroupId); + entityToPlaceInHands = Spawn(proto, coords); } if (component.Sound != null) diff --git a/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs b/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs index f42abf5e55..3b6e4d57d6 100644 --- a/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs +++ b/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs @@ -1,6 +1,7 @@ using Content.Server.Storage.Components; using Robust.Shared.Random; using System.Linq; +using Content.Shared.Storage; namespace Content.Server.Storage.EntitySystems; @@ -18,67 +19,15 @@ public sealed partial class StorageSystem var coordinates = Transform(uid).Coordinates; - var orGroupedSpawns = new Dictionary(); - - // collect groups together, create singular items that pass probability - foreach (var entry in component.Contents) + var spawnItems = EntitySpawnCollection.GetSpawns(component.Contents, _random); + foreach (var item in spawnItems) { - // Handle "Or" groups - if (!string.IsNullOrEmpty(entry.GroupId)) - { - if (!orGroupedSpawns.TryGetValue(entry.GroupId, out OrGroup? orGroup)) - { - orGroup = new(); - orGroupedSpawns.Add(entry.GroupId, orGroup); - } - orGroup.Entries.Add(entry); - orGroup.CumulativeProbability += entry.SpawnProbability; - continue; - } + var ent = EntityManager.SpawnEntity(item, coordinates); - // else - // Check random spawn - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (entry.SpawnProbability != 1f && !_random.Prob(entry.SpawnProbability)) continue; + if (storage.Insert(ent)) continue; - for (var i = 0; i < entry.Amount; i++) - { - var ent = EntityManager.SpawnEntity(entry.PrototypeId, coordinates); - - if (storage.Insert(ent)) continue; - - Logger.ErrorS("storage", $"Tried to StorageFill {entry.PrototypeId} inside {uid} but can't."); - EntityManager.DeleteEntity(ent); - } + Logger.ErrorS("storage", $"Tried to StorageFill {item} inside {uid} but can't."); + EntityManager.DeleteEntity(ent); } - - // handle orgroup spawns - foreach (var spawnValue in orGroupedSpawns.Values) - { - // For each group use the added cumulative probability to roll a double in that range - double diceRoll = _random.NextDouble() * spawnValue.CumulativeProbability; - // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item. - double cumulative = 0.0; - foreach (var entry in spawnValue.Entries) - { - cumulative += entry.SpawnProbability; - if (diceRoll > cumulative) continue; - // Dice roll succeeded, spawn item and break loop - for (var index = 0; index < entry.Amount; index++) - { - var ent = EntityManager.SpawnEntity(entry.PrototypeId, coordinates); - if (storage.Insert(ent)) continue; - Logger.ErrorS("storage", $"Tried to StorageFill {entry.PrototypeId} inside {uid} but can't."); - EntityManager.DeleteEntity(ent); - } - break; - } - } - } - - private sealed class OrGroup - { - public List Entries { get; set; } = new(); - public float CumulativeProbability { get; set; } = 0f; } } diff --git a/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs b/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs index a50b3e8aea..a2c5ecca66 100644 --- a/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs +++ b/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.DragDrop; +using Content.Shared.Storage; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager.Attributes; @@ -13,14 +14,9 @@ namespace Content.Shared.Nutrition.Components [RegisterComponent] public sealed class SharedButcherableComponent : Component, IDraggable { - //TODO: List for sub-products like animal-hides, organs and etc? [ViewVariables] - [DataField("spawned", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string SpawnedPrototype = "FoodMeat"; - - [ViewVariables] - [DataField("pieces")] - public int Pieces = 5; + [DataField("spawned", required: true)] + public List SpawnedEntities = new(); [DataField("butcherDelay")] public float ButcherDelay = 8.0f; diff --git a/Content.Shared/Storage/EntitySpawnEntry.cs b/Content.Shared/Storage/EntitySpawnEntry.cs new file mode 100644 index 0000000000..19c414da0c --- /dev/null +++ b/Content.Shared/Storage/EntitySpawnEntry.cs @@ -0,0 +1,132 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Storage; + +/// +/// Dictates a list of items that can be spawned. +/// +[Serializable] +[DataDefinition] +public struct EntitySpawnEntry : IPopulateDefaultValues +{ + [DataField("id", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string PrototypeId; + + /// + /// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc. + /// + [DataField("prob")] public float SpawnProbability; + + /// + /// orGroup signifies to pick between entities designated with an ID. + /// + /// + /// To define an orGroup in a StorageFill component you + /// need to add it to the entities you want to choose between and + /// add a prob field. In this example there is a 50% chance the storage + /// spawns with Y or Z. + /// + /// + /// - type: StorageFill + /// contents: + /// - name: X + /// - name: Y + /// prob: 0.50 + /// orGroup: YOrZ + /// - name: Z + /// orGroup: YOrZ + /// + /// + /// + [DataField("orGroup")] public string? GroupId; + + [DataField("amount")] public int Amount; + + public void PopulateDefaultValues() + { + Amount = 1; + SpawnProbability = 1; + } +} + +public static class EntitySpawnCollection +{ + private sealed class OrGroup + { + public List Entries { get; set; } = new(); + public float CumulativeProbability { get; set; } = 0f; + } + + /// + /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection. + /// + /// + /// This does not spawn the entities. The caller is responsible for doing so, since it may want to do something + /// special to those entities (offset them, insert them into storage, etc) + /// + /// The entity spawn entries. + /// Resolve param. + /// A list of entity prototypes that should be spawned. + public static List GetSpawns(IEnumerable entries, + IRobustRandom? random = null) + { + IoCManager.Resolve(ref random); + + var spawned = new List(); + var orGroupedSpawns = new Dictionary(); + + // collect groups together, create singular items that pass probability + foreach (var entry in entries) + { + // Handle "Or" groups + if (!string.IsNullOrEmpty(entry.GroupId)) + { + if (!orGroupedSpawns.TryGetValue(entry.GroupId, out OrGroup? orGroup)) + { + orGroup = new(); + orGroupedSpawns.Add(entry.GroupId, orGroup); + } + + orGroup.Entries.Add(entry); + orGroup.CumulativeProbability += entry.SpawnProbability; + continue; + } + + // else + // Check random spawn + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (entry.SpawnProbability != 1f && !random.Prob(entry.SpawnProbability)) continue; + + for (var i = 0; i < entry.Amount; i++) + { + spawned.Add(entry.PrototypeId); + } + } + + // handle orgroup spawns + foreach (var spawnValue in orGroupedSpawns.Values) + { + // For each group use the added cumulative probability to roll a double in that range + double diceRoll = random.NextDouble() * spawnValue.CumulativeProbability; + // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item. + var cumulative = 0.0; + foreach (var entry in spawnValue.Entries) + { + cumulative += entry.SpawnProbability; + if (diceRoll > cumulative) continue; + // Dice roll succeeded, add item and break loop + for (var index = 0; index < entry.Amount; index++) + { + spawned.Add(entry.PrototypeId); + } + + break; + } + } + + return spawned; + } +} diff --git a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl index dd41ca57b2..9fd80dbf9c 100644 --- a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl @@ -15,4 +15,4 @@ comp-kitchen-spike-knife-needed = You need a knife to do this. comp-kitchen-spike-remove-meat = You remove some meat from { THE($victim) }. comp-kitchen-spike-remove-meat-last = You remove the last piece of meat from { THE($victim) }! -comp-kitchen-spike-meat-name = { $victim } meat +comp-kitchen-spike-meat-name = { $name } ({ $victim }) diff --git a/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml b/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml index 597cf162b8..da6a584513 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml @@ -12,5 +12,6 @@ state: icon - type: Butcherable butcheringType: Knife - spawned: MaterialCloth1 - pieces: 2 + spawned: + - id: MaterialCloth1 + amount: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml b/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml index 5e2aab9f77..247f7eb5fa 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml @@ -10,5 +10,6 @@ state: icon - type: Butcherable butcheringType: Knife - spawned: MaterialCloth1 - pieces: 1 + spawned: + - id: MaterialCloth1 + amount: 1 diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml index 9a2716d448..f5fbe03dae 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml @@ -22,8 +22,9 @@ path: /Audio/Items/jumpsuit_equip.ogg - type: Butcherable butcheringType: Knife - spawned: MaterialCloth1 - pieces: 3 + spawned: + - id: MaterialCloth1 + amount: 3 - type: entity abstract: true @@ -39,5 +40,6 @@ path: /Audio/Items/jumpsuit_equip.ogg - type: Butcherable butcheringType: Knife - spawned: MaterialCloth1 - pieces: 3 + spawned: + - id: MaterialCloth1 + amount: 3 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 2be51e2f95..43e40b54a2 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -36,8 +36,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: InteractionPopup successChance: 0.2 interactSuccessString: petting-success-soft-floofy @@ -139,8 +140,9 @@ crit: dead-0 dead: dead-0 - type: Butcherable - spawned: FoodMeatChicken - pieces: 1 + spawned: + - id: FoodMeatChicken + amount: 1 - type: InteractionPopup successChance: 0.8 interactSuccessString: petting-success-bird @@ -169,8 +171,9 @@ crit: dead-0 dead: dead-0 - type: Butcherable - spawned: FoodMeatDuck - pieces: 1 + spawned: + - id: FoodMeatDuck + amount: 1 - type: InteractionPopup successChance: 0.9 interactSuccessString: petting-success-bird @@ -199,8 +202,9 @@ crit: dead-1 dead: dead-1 - type: Butcherable - spawned: FoodMeatDuck - pieces: 1 + spawned: + - id: FoodMeatDuck + amount: 1 - type: InteractionPopup successChance: 0.9 interactSuccessString: petting-success-bird @@ -229,8 +233,9 @@ crit: dead-2 dead: dead-2 - type: Butcherable - spawned: FoodMeatDuck - pieces: 1 + spawned: + - id: FoodMeatDuck + amount: 1 - type: InteractionPopup successChance: 0.9 interactSuccessString: petting-success-bird @@ -336,8 +341,9 @@ quantity: 25 updateRate: 30 - type: Butcherable - spawned: FoodMeat - pieces: 5 + spawned: + - id: FoodMeat + amount: 5 - type: Grammar attributes: gender: female # Here because of UdderComponent @@ -380,8 +386,9 @@ dead: dead - type: AsteroidRockVisualizer - type: Butcherable - spawned: FoodMeatCrab - pieces: 2 + spawned: + - id: FoodMeatCrab + amount: 2 - type: InteractionPopup successChance: 0.5 interactSuccessString: petting-success-crab @@ -420,8 +427,9 @@ quantity: 25 updateRate: 20 - type: Butcherable - spawned: FoodMeat - pieces: 4 + spawned: + - id: FoodMeat + amount: 4 - type: Grammar attributes: gender: female # Here because of UdderComponent @@ -450,8 +458,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeatChicken - pieces: 2 + spawned: + - id: FoodMeatChicken + amount: 2 - type: InteractionPopup # TODO: Make it so there's a separate chance to make certain animals outright hostile towards you. successChance: 0.1 # Yeah, good luck with that. interactSuccessString: petting-success-goose @@ -491,8 +500,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeat - pieces: 4 + spawned: + - id: FoodMeat + amount: 4 - type: Bloodstream bloodMaxVolume: 300 @@ -602,8 +612,9 @@ normalState: Monkey_burning - type: Butcherable butcheringType: Spike - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: MonkeyAccent - type: entity @@ -667,8 +678,9 @@ - ReagentId: Blood Quantity: 50 - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: ReplacementAccent accent: mouse - type: Tag @@ -770,8 +782,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: InteractionPopup successChance: 0.3 interactSuccessString: petting-success-reptile @@ -817,8 +830,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: InteractionPopup successChance: 0.6 interactSuccessString: petting-success-frog @@ -862,8 +876,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: InteractionPopup successChance: 0.6 interactSuccessString: petting-success-bird @@ -903,8 +918,9 @@ crit: penguin_dead dead: penguin_dead - type: Butcherable - spawned: FoodMeatPenguin - pieces: 3 + spawned: + - id: FoodMeatPenguin + amount: 3 - type: InteractionPopup successChance: 0.5 interactSuccessString: petting-success-bird @@ -951,8 +967,9 @@ crit: dead dead: dead - type: Butcherable - spawned: FoodMeatPenguin - pieces: 3 + spawned: + - id: FoodMeatPenguin + amount: 3 - type: UnarmedCombat range: 0.5 arcwidth: 0 @@ -965,7 +982,7 @@ - type: OnUseTimerTrigger delay: 10 beepSound: - path: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg #funny sfx use + path: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg #funny sfx use beepInterval: 1 - type: Explosive devastationRange: 1 @@ -1009,8 +1026,9 @@ # dead: dead # crit: dead - type: Butcherable - spawned: FoodMeat - pieces: 1 + spawned: + - id: FoodMeat + amount: 1 - type: InteractionPopup successChance: 0.6 interactSuccessString: petting-success-reptile @@ -1055,8 +1073,9 @@ crit: tarantula_dead dead: tarantula_dead - type: Butcherable - spawned: FoodMeatSpider - pieces: 2 + spawned: + - id: FoodMeatSpider + amount: 2 - type: InteractionPopup successChance: 0.5 interactSuccessString: petting-success-tarantula @@ -1108,8 +1127,9 @@ crit: possum_dead # TODO: Make it so possums can "play dead." Probably need AI changes dead: possum_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 0.2 # Low when undomesticated. interactSuccessString: petting-success-possum # Possums don't really make much noise when they're happy. They make clicking noises as a mating call, but that is NOT the same thing! @@ -1166,8 +1186,9 @@ crit: raccoon_dead dead: raccoon_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 0.2 # Low when undomesticated. interactSuccessString: petting-success-soft-floofy @@ -1222,8 +1243,9 @@ crit: fox_dead dead: fox_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 0.5 interactSuccessString: petting-success-soft-floofy diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 39ae0a1a13..0c928c9f3e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -43,8 +43,10 @@ crit: crit dead: dead - type: Butcherable - spawned: FoodMeat # TODO: CrapMeat or FishMeat # - 2022-02-17 LMAO crap meat - pieces: 2 + # TODO: CrapMeat or FishMeat # - 2022-02-17 LMAO crap meat + spawned: + - id: FoodMeat + amount: 2 - type: UnarmedCombat range: 1.5 arcwidth: 0 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 733fea7831..a64e83b8bc 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -33,8 +33,9 @@ crit: corgi_dead dead: corgi_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: ReplacementAccent accent: dog - type: InteractionPopup @@ -211,8 +212,9 @@ crit: cat_dead dead: cat_dead - type: Butcherable - spawned: FoodMeat - pieces: 2 + spawned: + - id: FoodMeat + amount: 3 - type: ReplacementAccent accent: cat - type: InteractionPopup @@ -362,8 +364,9 @@ crit: sloth_dead dead: sloth_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 0.9 interactSuccessString: petting-success-sloth @@ -405,8 +408,9 @@ crit: ferret_dead dead: ferret_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 0.8 interactDelay: 1.5 # Avoids overlapping SFX due to spam - these SFX are a little longer than the typical 1 second. @@ -449,7 +453,7 @@ mass: 10 mask: - Impassable - - MobImpassable + - MobImpassable - VaultImpassable - SmallImpassable layer: @@ -461,10 +465,11 @@ crit: bingus_dead dead: bingus_dead - type: Butcherable - spawned: FoodMeat - pieces: 2 + spawned: + - id: FoodMeat + amount: 2 - type: InteractionPopup - successChance: 0.9 + successChance: 0.9 interactSuccessString: petting-success-cat interactFailureString: petting-failure-generic interactSuccessSound: @@ -472,7 +477,7 @@ - type: Grammar attributes: gender: epicene - + - type: entity name: mcgriff parent: SimpleMobBase @@ -494,7 +499,7 @@ mass: 10 mask: - Impassable - - MobImpassable + - MobImpassable - SmallImpassable - VaultImpassable layer: @@ -506,12 +511,13 @@ crit: mcgriff_dead dead: mcgriff_dead - type: Butcherable - spawned: FoodMeat - pieces: 2 + spawned: + - id: FoodMeat + amount: 2 - type: ReplacementAccent accent: dog - type: InteractionPopup - successChance: 0.5 + successChance: 0.5 interactSuccessString: petting-success-dog interactFailureString: petting-failure-generic interactSuccessSound: @@ -540,8 +546,9 @@ crit: paperwork_dead dead: paperwork_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: InteractionPopup successChance: 1 interactSuccessString: petting-success-sloth @@ -550,7 +557,7 @@ attributes: proper: true gender: male - + - type: entity name: walter parent: SimpleMobBase @@ -572,7 +579,7 @@ mass: 10 mask: - Impassable - - MobImpassable + - MobImpassable - SmallImpassable - VaultImpassable layer: @@ -584,8 +591,9 @@ crit: walter_dead dead: walter_dead - type: Butcherable - spawned: FoodMeat - pieces: 3 + spawned: + - id: FoodMeat + amount: 3 - type: ReplacementAccent accent: dog - type: InteractionPopup @@ -597,4 +605,4 @@ - type: Grammar attributes: proper: true - gender: male \ No newline at end of file + gender: male diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml b/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml index 7120d807d4..4f555d9ce7 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml @@ -45,8 +45,9 @@ normal: alive dead: dead - type: Butcherable - spawned: FoodMeatXeno - pieces: 1 + spawned: + - id: FoodMeatXeno + amount: 1 - type: Bloodstream bloodMaxVolume: 50 - type: UnarmedCombat diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 9b1587732a..3c880dd589 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -69,9 +69,10 @@ dead: dead - type: Puller - type: Butcherable - spawned: FoodMeatXeno butcheringType: Spike - pieces: 5 + spawned: + - id: FoodMeatXeno + amount: 5 - type: GhostTakeoverAvailable makeSentient: true name: xeno diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index fbb1c9e82d..dae8a37952 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -284,7 +284,9 @@ - type: Puller - type: Butcherable butcheringType: Spike # TODO human. - spawned: FoodMeat + spawned: + - id: FoodMeat + amount: 5 # - type: Recyclable Turns out turning off recycler safeties without considering the instagib is a bad idea # safe: false - type: Speech diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index fa27bbf81f..3bf0cba0bb 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -136,7 +136,9 @@ probability: 0.25 - type: Butcherable butcheringType: Spike - spawned: FoodMeatSlime + spawned: + - id: FoodMeatSlime + amount: 5 - type: entity save: false diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index a0afe576fc..dc95e4c244 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -111,4 +111,6 @@ speciesId: vox - type: Butcherable butcheringType: Spike - spawned: FoodMeatChicken + spawned: + - id: FoodMeatChicken + amount: 5