diff --git a/Content.Shared/Nutrition/Components/EdibleComponent.cs b/Content.Shared/Nutrition/Components/EdibleComponent.cs
index f689aa8b99af..b10b26dc18ad 100644
--- a/Content.Shared/Nutrition/Components/EdibleComponent.cs
+++ b/Content.Shared/Nutrition/Components/EdibleComponent.cs
@@ -78,6 +78,14 @@ public sealed partial class EdibleComponent : Component
[DataField]
public bool RequireDead = true;
+ ///
+ /// Maximum hunger value this food can bring the eater to.
+ /// If null, there is no cap (fully satisfying food, e.g. cooked meals).
+ /// If set, food acts as a snack and cannot push hunger above this value.
+ ///
+ [DataField]
+ public float? MaxNutrition = null;
+
///
/// Verb, icon, and sound data for our edible.
///
diff --git a/Content.Shared/Nutrition/Components/HungerComponent.cs b/Content.Shared/Nutrition/Components/HungerComponent.cs
index 85b40769ab60..db50093020f2 100644
--- a/Content.Shared/Nutrition/Components/HungerComponent.cs
+++ b/Content.Shared/Nutrition/Components/HungerComponent.cs
@@ -83,6 +83,16 @@ public sealed partial class HungerComponent : Component
[DataField, ViewVariables(VVAccess.ReadOnly)]
public float? StartingHunger = null;
+ ///
+ /// Goobstation
+ /// Active hunger cap set when eating snack food (food with EdibleComponent.MaxNutrition).
+ /// Hunger cannot be raised above this value while the cap is active.
+ /// Cleared to null when eating fully satisfying (cooked) food.
+ ///
+ [DataField]
+ [AutoNetworkedField]
+ public float? NutritionCap = null;
+
///
/// A dictionary relating HungerThreshold to the amount of current hunger needed for each one
///
diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
index 767e8e72f9df..eddb5c1f745f 100644
--- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
+++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
@@ -182,7 +182,10 @@ public void ModifyHunger(EntityUid uid, float amount, HungerComponent? component
{
if (!Resolve(uid, ref component))
return;
- SetHunger(uid, GetHunger(component) + amount, component);
+ var newHunger = GetHunger(component) + amount;
+ if (component.NutritionCap.HasValue)
+ newHunger = Math.Min(newHunger, component.NutritionCap.Value);
+ SetHunger(uid, newHunger, component);
}
///
@@ -299,6 +302,28 @@ public HungerThreshold GetHungerThreshold(HungerComponent component, float? food
return result;
}
+ ///
+ /// Goobstation — sets or raises the active nutrition cap for snack food.
+ ///
+ public void SetNutritionCap(EntityUid uid, float cap, HungerComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+ component.NutritionCap = Math.Max(component.NutritionCap ?? 0f, cap);
+ DirtyField(uid, component, nameof(HungerComponent.NutritionCap));
+ }
+
+ ///
+ /// Goobstation — clears the active nutrition cap (called when eating fully satisfying food).
+ ///
+ public void ClearNutritionCap(EntityUid uid, HungerComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+ component.NutritionCap = null;
+ DirtyField(uid, component, nameof(HungerComponent.NutritionCap));
+ }
+
///
/// A check that returns if the entity is below a hunger threshold.
///
diff --git a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs
index 4814dec2647b..72f80a54a2fa 100644
--- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs
+++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs
@@ -60,6 +60,7 @@ public sealed partial class IngestionSystem : EntitySystem
[Dependency] private readonly SharedBodySystem _body = default!;
[Dependency] private readonly ReactiveSystem _reaction = default!;
[Dependency] private readonly StomachSystem _stomach = default!;
+ [Dependency] private readonly HungerSystem _hungerSystem = default!;
///
public override void Initialize()
@@ -368,6 +369,15 @@ private void OnEatingDoAfter(Entity entity, ref EatingDoAfterEven
_reaction.DoEntityReaction(entity, split, ReactionMethod.Ingestion);
+ // Goobstation — update NutritionCap based on whether this food is a snack
+ if (TryComp(food, out EdibleComponent? edibleComp))
+ {
+ if (edibleComp.MaxNutrition.HasValue)
+ _hungerSystem.SetNutritionCap(entity, edibleComp.MaxNutrition.Value);
+ else
+ _hungerSystem.ClearNutritionCap(entity);
+ }
+
// Everything is good to go item has been successfuly eaten
var afterEv = new IngestedEvent(args.User, entity, split, forceFed);
RaiseLocalEvent(food, ref afterEv);
diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_food.yml b/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
index 28e6818c7b0f..00bfecc4b343 100644
--- a/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
+++ b/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
@@ -13,26 +13,6 @@
#
# SPDX-License-Identifier: MIT
-- type: cargoProduct
- id: FoodPizza
- icon:
- sprite: Objects/Consumable/Food/Baked/pizza.rsi
- state: margherita-slice
- product: CrateFoodPizza
- cost: 1600
- category: cargoproduct-category-name-food
- group: market
-
-- type: cargoProduct
- id: FoodPizzaLarge
- icon:
- sprite: Objects/Consumable/Food/Baked/pizza.rsi
- state: margherita
- product: CrateFoodPizzaLarge
- cost: 1800
- category: cargoproduct-category-name-food
- group: market
-
- type: cargoProduct
id: FoodMRE
icon:
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml
index e1f0555187d5..73dda8ada245 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/snacks.yml
@@ -131,6 +131,8 @@
- type: Tag
tags:
- FoodSnack
+ - type: Edible
+ maxNutrition: 100.0
- type: Sprite
sprite: Objects/Consumable/Food/snacks.rsi
- type: SolutionContainerManager
diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml
index 9fba7eca5125..01acb6fa995a 100644
--- a/Resources/Prototypes/GameRules/cargo_gifts.yml
+++ b/Resources/Prototypes/GameRules/cargo_gifts.yml
@@ -24,8 +24,6 @@
- id: GiftsFireProtection
- id: GiftsJanitor
- id: GiftsMedical
- - id: GiftsPizzaPartyLarge
- - id: GiftsPizzaPartySmall
- id: GiftsSecurityGuns
- id: GiftsSecurityRiot
- id: GiftsSpacingSupplies
@@ -49,54 +47,6 @@
- type: CargoGiftsRule
sender: cargo-gift-default-sender
-- type: entity
- id: GiftsPizzaPartySmall
- parent: CargoGiftsBase
- components:
- - type: StationEvent
- weight: 5
- duration: 120
- earliestStart: 20
- chaos: # Goobstation
- Hunger: -80
- Thirst: -40
- eventType: FreeStuff # Goobstation
- # Goobstation
- - type: GameRule
- chaosScore: -15
- - type: CargoGiftsRule
- description: cargo-gift-pizza-small
- dest: cargo-gift-dest-bar
- gifts:
- FoodPizza: 2 # 8 pizzas
- FoodBarSupply: 1
- FoodSoftdrinks: 2
- CrateVendingMachineRestockRobustSoftdrinks: 1
-
-- type: entity
- id: GiftsPizzaPartyLarge
- parent: CargoGiftsBase
- components:
- - type: StationEvent
- weight: 2
- duration: 240
- earliestStart: 20
- minimumPlayers: 40
- chaos: # Goobstation
- Hunger: -300
- Thirst: -120
- eventType: FreeStuff # Goobstation
- # Goobstation
- - type: GameRule
- chaosScore: -30
- - type: CargoGiftsRule
- description: cargo-gift-pizza-large
- dest: cargo-gift-dest-bar
- gifts:
- FoodPizzaLarge: 1 # 16 pizzas
- FoodBarSupply: 1
- FoodSoftdrinksLarge: 1
-
- type: entity
id: GiftsEngineering
parent: CargoGiftsBase
diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml
index 59b6b06055d0..aeff2f2857c3 100644
--- a/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml
+++ b/Resources/Prototypes/Recipes/Crafting/Graphs/storage/tallbox.yml
@@ -177,6 +177,8 @@
conditions:
- !type:StorageWelded
welded: false
+ - !type:Locked
+ locked: false
completed:
- !type:SpawnPrototype
prototype: SheetSteel1
diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Consumable/Food/food_base_snack.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Consumable/Food/food_base_snack.yml
new file mode 100644
index 000000000000..f7dbe698b44f
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Consumable/Food/food_base_snack.yml
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: 2025 Goob-Station
+#
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# FoodSnackBase is defined in upstream snacks.yml with maxNutrition via Edible component.
+# No additional definitions needed here.