Lipid Extractor (#15597)

This commit is contained in:
Nemanja
2023-04-22 03:03:50 -04:00
committed by GitHub
parent 0859101afe
commit 4f9ae1116e
22 changed files with 486 additions and 2 deletions

View File

@@ -0,0 +1,94 @@
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Nutrition.Components;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Nutrition.Components;
/// <summary>
/// This is used for a machine that extracts hunger from entities and creates meat. Yum!
/// </summary>
[RegisterComponent, Access(typeof(FatExtractorSystem))]
public sealed class FatExtractorComponent : Component
{
/// <summary>
/// Whether or not the extractor is currently extracting fat from someone
/// </summary>
[DataField("processing")]
public bool Processing = true;
/// <summary>
/// How much nutrition is extracted per second.
/// </summary>
[DataField("nutritionPerSecond"), ViewVariables(VVAccess.ReadWrite)]
public int NutritionPerSecond = 10;
/// <summary>
/// The base rate of extraction
/// </summary>
[DataField("baseNutritionPerSecond"), ViewVariables(VVAccess.ReadWrite)]
public int BaseNutritionPerSecond = 10;
#region Machine Upgrade
/// <summary>
/// Which machine part affects the nutrition rate
/// </summary>
[DataField("machinePartNutritionRate", customTypeSerializer: typeof(PrototypeIdSerializer<MachinePartPrototype>))]
public string MachinePartNutritionRate = "Laser";
/// <summary>
/// The increase in rate per each rating above 1.
/// </summary>
[DataField("partRatingRateMultiplier")]
public float PartRatingRateMultiplier = 10;
#endregion
/// <summary>
/// An accumulator which tracks extracted nutrition to determine
/// when to spawn a meat.
/// </summary>
[DataField("nutrientAccumulator"), ViewVariables(VVAccess.ReadWrite)]
public int NutrientAccumulator;
/// <summary>
/// How high <see cref="NutrientAccumulator"/> has to be to spawn meat
/// </summary>
[DataField("nutrientPerMeat"), ViewVariables(VVAccess.ReadWrite)]
public int NutrientPerMeat = 60;
/// <summary>
/// Meat spawned by the extractor.
/// </summary>
[DataField("meatPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
public string MeatPrototype = "FoodMeat";
/// <summary>
/// When the next update will occur
/// </summary>
[DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan NextUpdate;
/// <summary>
/// How long each update takes
/// </summary>
[DataField("updateTime"), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan UpdateTime = TimeSpan.FromSeconds(1);
/// <summary>
/// The sound played when extracting
/// </summary>
[DataField("processSound")]
public SoundSpecifier? ProcessSound;
public IPlayingAudioStream? Stream;
/// <summary>
/// A minium hunger threshold for extracting nutrition.
/// Ignored when emagged.
/// </summary>
[DataField("minHungerThreshold")]
public HungerThreshold MinHungerThreshold = HungerThreshold.Okay;
}

View File

@@ -0,0 +1,164 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Construction;
using Content.Server.Nutrition.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Storage.Components;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Storage.Components;
using Robust.Shared.Timing;
namespace Content.Server.Nutrition.EntitySystems;
/// <summary>
/// This handles logic and interactions relating to <see cref="FatExtractorComponent"/>
/// </summary>
public sealed class FatExtractorSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly HungerSystem _hunger = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<FatExtractorComponent, RefreshPartsEvent>(OnRefreshParts);
SubscribeLocalEvent<FatExtractorComponent, UpgradeExamineEvent>(OnUpgradeExamine);
SubscribeLocalEvent<FatExtractorComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<FatExtractorComponent, GotEmaggedEvent>(OnGotEmagged);
SubscribeLocalEvent<FatExtractorComponent, StorageAfterCloseEvent>(OnClosed);
SubscribeLocalEvent<FatExtractorComponent, StorageAfterOpenEvent>(OnOpen);
SubscribeLocalEvent<FatExtractorComponent, PowerChangedEvent>(OnPowerChanged);
}
private void OnRefreshParts(EntityUid uid, FatExtractorComponent component, RefreshPartsEvent args)
{
var rating = args.PartRatings[component.MachinePartNutritionRate] - 1;
component.NutritionPerSecond = component.BaseNutritionPerSecond + (int) (component.PartRatingRateMultiplier * rating);
}
private void OnUpgradeExamine(EntityUid uid, FatExtractorComponent component, UpgradeExamineEvent args)
{
args.AddPercentageUpgrade("fat-extractor-component-rate", (float) component.NutritionPerSecond / component.BaseNutritionPerSecond);
}
private void OnUnpaused(EntityUid uid, FatExtractorComponent component, ref EntityUnpausedEvent args)
{
component.NextUpdate += args.PausedTime;
}
private void OnGotEmagged(EntityUid uid, FatExtractorComponent component, ref GotEmaggedEvent args)
{
args.Handled = true;
args.Repeatable = false;
}
private void OnClosed(EntityUid uid, FatExtractorComponent component, ref StorageAfterCloseEvent args)
{
StartProcessing(uid, component);
}
private void OnOpen(EntityUid uid, FatExtractorComponent component, ref StorageAfterOpenEvent args)
{
StopProcessing(uid, component);
}
private void OnPowerChanged(EntityUid uid, FatExtractorComponent component, ref PowerChangedEvent args)
{
if (!args.Powered)
StopProcessing(uid, component);
}
public void StartProcessing(EntityUid uid, FatExtractorComponent? component = null, EntityStorageComponent? storage = null)
{
if (!Resolve(uid, ref component, ref storage))
return;
if (component.Processing)
return;
if (!this.IsPowered(uid, EntityManager))
return;
if (!TryGetValidOccupant(uid, out _, component, storage))
return;
component.Processing = true;
_appearance.SetData(uid, FatExtractorVisuals.Processing, true);
component.Stream = _audio.PlayPvs(component.ProcessSound, uid);
component.NextUpdate = _timing.CurTime + component.UpdateTime;
}
public void StopProcessing(EntityUid uid, FatExtractorComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (!component.Processing)
return;
component.Processing = false;
_appearance.SetData(uid, FatExtractorVisuals.Processing, false);
component.Stream?.Stop();
}
public bool TryGetValidOccupant(EntityUid uid, [NotNullWhen(true)] out EntityUid? occupant, FatExtractorComponent? component = null, EntityStorageComponent? storage = null)
{
occupant = null;
if (!Resolve(uid, ref component, ref storage))
return false;
occupant = storage.Contents.ContainedEntities.FirstOrDefault();
if (!TryComp<HungerComponent>(occupant, out var hunger))
return false;
if (hunger.CurrentHunger < component.NutritionPerSecond)
return false;
if (hunger.CurrentThreshold < component.MinHungerThreshold && !HasComp<EmaggedComponent>(uid))
return false;
return true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<FatExtractorComponent, EntityStorageComponent>();
while (query.MoveNext(out var uid, out var fat, out var storage))
{
if (TryGetValidOccupant(uid, out var occupant, fat, storage))
{
if (!fat.Processing)
StartProcessing(uid, fat, storage);
}
else
{
StopProcessing(uid, fat);
continue;
}
if (!fat.Processing)
continue;
if (_timing.CurTime < fat.NextUpdate)
continue;
fat.NextUpdate += fat.UpdateTime;
_hunger.ModifyHunger(occupant.Value, -fat.NutritionPerSecond);
fat.NutrientAccumulator += fat.NutritionPerSecond;
if (fat.NutrientAccumulator >= fat.NutrientPerMeat)
{
fat.NutrientAccumulator -= fat.NutrientPerMeat;
Spawn(fat.MeatPrototype, Transform(uid).Coordinates);
}
}
}
}

View File

@@ -34,6 +34,12 @@ public sealed class LockComponent : Component
/// </summary> /// </summary>
[DataField("lockingSound"), ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound"), ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier LockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_on.ogg"); public SoundSpecifier LockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_on.ogg");
/// <summary>
/// Whether or not an emag disables it.
/// </summary>
[DataField("breakOnEmag")]
public bool BreakOnEmag = true;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -203,7 +203,7 @@ public sealed class LockSystem : EntitySystem
private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? reader = null, bool quiet = true) private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? reader = null, bool quiet = true)
{ {
// Not having an AccessComponent means you get free access. woo! // Not having an AccessComponent means you get free access. woo!
if (!Resolve(uid, ref reader)) if (!Resolve(uid, ref reader, false))
return true; return true;
if (_accessReader.IsAllowed(user, reader)) if (_accessReader.IsAllowed(user, reader))
@@ -234,7 +234,7 @@ public sealed class LockSystem : EntitySystem
private void OnEmagged(EntityUid uid, LockComponent component, ref GotEmaggedEvent args) private void OnEmagged(EntityUid uid, LockComponent component, ref GotEmaggedEvent args)
{ {
if (!component.Locked) if (!component.Locked || !component.BreakOnEmag)
return; return;
_audio.PlayPredicted(component.UnlockSound, uid, null, AudioParams.Default.WithVolume(-5)); _audio.PlayPredicted(component.UnlockSound, uid, null, AudioParams.Default.WithVolume(-5));
_appearanceSystem.SetData(uid, StorageVisuals.Locked, false); _appearanceSystem.SetData(uid, StorageVisuals.Locked, false);

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Nutrition.Components;
[Serializable, NetSerializable]
public enum FatExtractorVisuals : byte
{
Processing
}
public enum FatExtractorVisualLayers : byte
{
Light,
Stack,
Smoke
}

View File

@@ -0,0 +1,8 @@
fat-extractor-component-rate = extraction rate
fat-extractor-fact-1 = Fats are triglycerides made up of a combination of different building blocks; glycerol and fatty acids.
fat-extractor-fact-2 = Adults should get a recommended 20-35% of their energy intake from fat.
fat-extractor-fact-3 = Being overweight or obese puts you at an increased risk of chronic diseases, such as cardiovascular diseases, metabolic syndrome, type 2 diabetes, and some types of cancers.
fat-extractor-fact-4 = Not all fats are bad. A certain amount of fat is an essential part of a healthy balanced diet.
fat-extractor-fact-5 = Saturated fat should form no more than 11% of your daily calories.
fat-extractor-fact-6 = Unsaturated fat, that is monounsaturated fats, polyunsaturated fats, and omega-3 fatty acids, is found in plants and fish.

View File

@@ -61,6 +61,7 @@
- MicrowaveMachineCircuitboard - MicrowaveMachineCircuitboard
- BoozeDispenserMachineCircuitboard - BoozeDispenserMachineCircuitboard
- SodaDispenserMachineCircuitboard - SodaDispenserMachineCircuitboard
- FatExtractorMachineCircuitboard
# Biological Technology Tree # Biological Technology Tree

View File

@@ -0,0 +1,9 @@
- type: advertisementsPack
id: FatExtractorFacts
advertisements:
- fat-extractor-fact-1
- fat-extractor-fact-2
- fat-extractor-fact-3
- fat-extractor-fact-4
- fat-extractor-fact-5
- fat-extractor-fact-6

View File

@@ -673,6 +673,23 @@
Glass: 2 Glass: 2
Cable: 2 Cable: 2
- type: entity
id: FatExtractorMachineCircuitboard
parent: BaseMachineCircuitboard
name: lipid extractor machine board
components:
- type: Sprite
state: service
- type: MachineBoard
prototype: FatExtractor
requirements:
Laser: 1
componentRequirements:
Utensil:
Amount: 1
DefaultPrototype: ForkPlastic
ExamineName: Utensil
- type: entity - type: entity
id: EmitterCircuitboard id: EmitterCircuitboard
parent: BaseMachineCircuitboard parent: BaseMachineCircuitboard

View File

@@ -0,0 +1,113 @@
- type: entity
id: FatExtractor
parent: BaseMachinePowered
name: lipid extractor
description: Safely and efficiently extracts excess fat from a subject.
components:
- type: FatExtractor
processSound:
path: /Audio/Machines/microwave_loop.ogg
params:
loop: true
maxdistance: 5
- type: Sprite
netsync: false
sprite: Structures/Machines/fat_sucker.rsi
snapCardinals: true
layers:
- state: fat
- state: fat_door_off
map: ["enum.StorageVisualLayers.Door"]
- state: fat_red
shader: unshaded
map: ["enum.PowerDeviceVisualLayers.Powered"]
- state: fat_green
shader: unshaded
visible: false
map: ["enum.FatExtractorVisualLayers.Light"]
- state: fat_panel
visible: false
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- state: fat_stack #cash cash cash
visible: false
map: ["enum.FatExtractorVisualLayers.Stack"]
- state: fat_smoke
visible: false
map: ["enum.FatExtractorVisualLayers.Smoke"]
- type: Lock
breakOnEmag: false
- type: GenericVisualizer
visuals:
enum.StorageVisuals.Open:
enum.StorageVisualLayers.Door:
True: { visible: false }
False: { visible: true }
enum.FatExtractorVisuals.Processing:
enum.StorageVisualLayers.Door:
True: { state: fat_door_on }
False: { state: fat_door_off }
enum.FatExtractorVisualLayers.Smoke:
True: { visible: true }
False: { visible: false }
enum.FatExtractorVisualLayers.Stack:
True: { visible: true }
False: { visible: false }
enum.FatExtractorVisualLayers.Light:
True: { visible: true }
False: { visible: false }
enum.PowerDeviceVisuals.Powered:
enum.FatExtractorVisualLayers.Light:
False: { visible: false }
enum.PowerDeviceVisualLayers.Powered:
True: { visible: true }
False: { visible: false }
enum.StorageVisuals.HasContents:
enum.PowerDeviceVisualLayers.Powered:
True: { state: fat_yellow }
False: { state: fat_red }
enum.WiresVisuals.MaintenancePanelState:
enum.WiresVisualLayers.MaintenancePanel:
True: { visible: true }
False: { visible: false }
- type: Construction
graph: Machine
node: machine
containers:
- machine_board
- machine_parts
- entity_storage
- type: EmptyOnMachineDeconstruct
containers:
- entity_storage
- type: Damageable
damageContainer: Inorganic
damageModifierSet: StrongMetallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 100
behaviors:
- !type:ChangeConstructionNodeBehavior
node: machineFrame
- !type:DoActsBehavior
acts: ["Destruction"]
- type: Machine
board: FatExtractorMachineCircuitboard
- type: Wires
BoardName: FatExtractor
LayoutId: FatExtractor
- type: Appearance
- type: Speech
- type: Advertise
pack: FatExtractorFacts
- type: StaticPrice
price: 1000
- type: ResistLocker
- type: EntityStorage
capacity: 1
- type: ContainerContainer
containers:
machine_board: !type:Container
machine_parts: !type:Container
entity_storage: !type:Container

View File

@@ -300,6 +300,7 @@
- ReagentGrinderMachineCircuitboard - ReagentGrinderMachineCircuitboard
- HotplateMachineCircuitboard - HotplateMachineCircuitboard
- MicrowaveMachineCircuitboard - MicrowaveMachineCircuitboard
- FatExtractorMachineCircuitboard
- UniformPrinterMachineCircuitboard - UniformPrinterMachineCircuitboard
- ShuttleConsoleCircuitboard - ShuttleConsoleCircuitboard
- RadarConsoleCircuitboard - RadarConsoleCircuitboard

View File

@@ -424,6 +424,14 @@
Steel: 100 Steel: 100
Glass: 900 Glass: 900
- type: latheRecipe
id: FatExtractorMachineCircuitboard
result: FatExtractorMachineCircuitboard
completetime: 4
materials:
Steel: 100
Glass: 900
- type: latheRecipe - type: latheRecipe
id: SurveillanceCameraRouterCircuitboard id: SurveillanceCameraRouterCircuitboard
result: SurveillanceCameraRouterCircuitboard result: SurveillanceCameraRouterCircuitboard

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

View File

@@ -0,0 +1,47 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from /tg/station at commit https://github.com/tgstation/tgstation/commit/48370e5a35a19eab427d3e403b653e65fa391ca2",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "fat"
},
{
"name": "fat_door_off"
},
{
"name": "fat_door_on"
},
{
"name": "fat_green"
},
{
"name": "fat_yellow"
},
{
"name": "fat_red"
},
{
"name": "fat_stack"
},
{
"name": "fat_smoke",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.4
]
]
},
{
"name": "fat_panel"
}
]
}