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.