--- /dev/null
+using Content.Shared.Conveyor;
+using Content.Shared.Materials;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Materials;
+
+public sealed class RecyclerVisualizerSystem : VisualizerSystem<RecyclerVisualsComponent>
+{
+ protected override void OnAppearanceChange(EntityUid uid, RecyclerVisualsComponent component, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null || !args.Sprite.LayerMapTryGet(RecyclerVisualLayers.Main, out var layer))
+ return;
+
+ AppearanceSystem.TryGetData<ConveyorState>(uid, ConveyorVisuals.State, out var running);
+ AppearanceSystem.TryGetData<bool>(uid, RecyclerVisuals.Bloody, out var bloody);
+ AppearanceSystem.TryGetData<bool>(uid, RecyclerVisuals.Broken, out var broken);
+
+ var activityState = running == ConveyorState.Off ? 0 : 1;
+ if (broken) //breakage overrides activity
+ activityState = 2;
+
+ var bloodyKey = bloody ? component.BloodyKey : string.Empty;
+
+ var state = $"{component.BaseKey}{activityState}{bloodyKey}";
+ args.Sprite.LayerSetState(layer, state);
+ }
+}
--- /dev/null
+namespace Content.Client.Materials;
+
+[RegisterComponent]
+public sealed partial class RecyclerVisualsComponent : Component
+{
+ /// <summary>
+ /// Key appended to state string if bloody.
+ /// </summary>
+ [DataField]
+ public string BloodyKey = "bld";
+
+ /// <summary>
+ /// Base key for the visual state.
+ /// </summary>
+ [DataField]
+ public string BaseKey = "grinder-o";
+}
using Robust.Shared.Utility;
using System.Linq;
using Content.Server.Administration.Logs;
+using Content.Server.Repairable;
using Content.Shared.Database;
+using Content.Shared.Destructible;
+using Content.Shared.Emag.Components;
+using Robust.Shared.Prototypes;
namespace Content.Server.Materials;
/// <inheritdoc/>
public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
[Dependency] private readonly OpenableSystem _openable = default!;
[Dependency] private readonly PopupSystem _popup = default!;
- [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
+ [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly SharedBodySystem _body = default!; //bobby
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly StackSystem _stack = default!;
{
base.Initialize();
- SubscribeLocalEvent<MaterialReclaimerComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<MaterialReclaimerComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<MaterialReclaimerComponent, InteractUsingEvent>(OnInteractUsing,
- before: new []{typeof(WiresSystem), typeof(SolutionTransferSystem)});
+ before: [typeof(WiresSystem), typeof(SolutionTransferSystem)]);
SubscribeLocalEvent<MaterialReclaimerComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
SubscribeLocalEvent<ActiveMaterialReclaimerComponent, PowerChangedEvent>(OnActivePowerChanged);
- }
- private void OnStartup(Entity<MaterialReclaimerComponent> entity, ref ComponentStartup args)
- {
- _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionContainerId);
+
+ SubscribeLocalEvent<MaterialReclaimerComponent, BreakageEventArgs>(OnBreakage);
+ SubscribeLocalEvent<MaterialReclaimerComponent, RepairedEvent>(OnRepaired);
}
private void OnPowerChanged(Entity<MaterialReclaimerComponent> entity, ref PowerChangedEvent args)
TryFinishProcessItem(entity, null, entity.Comp);
}
+ private void OnBreakage(Entity<MaterialReclaimerComponent> ent, ref BreakageEventArgs args)
+ {
+ //un-emags itself when it breaks
+ RemComp<EmaggedComponent>(ent);
+ SetBroken(ent, true);
+ }
+
+ private void OnRepaired(Entity<MaterialReclaimerComponent> ent, ref RepairedEvent args)
+ {
+ SetBroken(ent, false);
+ }
+
+ public void SetBroken(Entity<MaterialReclaimerComponent> ent, bool val)
+ {
+ if (ent.Comp.Broken == val)
+ return;
+
+ _appearance.SetData(ent, RecyclerVisuals.Broken, val);
+ SetReclaimerEnabled(ent, false);
+
+ ent.Comp.Broken = val;
+ Dirty(ent);
+ }
+
/// <inheritdoc/>
public override bool TryFinishProcessItem(EntityUid uid, MaterialReclaimerComponent? component = null, ActiveMaterialReclaimerComponent? active = null)
{
// scales the output if the process was interrupted.
var completion = 1f - Math.Clamp((float) Math.Round((active.EndTime - Timing.CurTime) / active.Duration),
- 0f, 1f);
+ 0f,
+ 1f);
Reclaim(uid, item, completion, component);
return true;
foreach (var (storedMaterial, storedAmount) in storage.Storage)
{
- var stacks = _materialStorage.SpawnMultipleFromMaterial(storedAmount, storedMaterial,
+ var stacks = _materialStorage.SpawnMultipleFromMaterial(storedAmount,
+ storedMaterial,
xform.Coordinates,
out var materialOverflow);
var amountConsumed = storedAmount - materialOverflow;
{
if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
return;
- if (!_solutionContainer.TryGetSolution(reclaimer, reclaimerComponent.SolutionContainerId, out var outputSolution))
- return;
efficiency *= reclaimerComponent.Efficiency;
}
// if the item we inserted has reagents, add it in.
- if (TryComp<SolutionContainerManagerComponent>(item, out var solutionContainer))
+ if (_solutionContainer.TryGetDrainableSolution(item, out _, out var drainableSolution))
{
- foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solutionContainer)))
- {
- var solution = soln.Comp.Solution;
- foreach (var quantity in solution.Contents)
- {
- totalChemicals.AddReagent(quantity.Reagent.Prototype, quantity.Quantity * efficiency, false);
- }
- }
+ totalChemicals.AddSolution(drainableSolution, _prototype);
}
- _solutionContainer.TryTransferSolution(outputSolution.Value, totalChemicals, totalChemicals.Volume);
- if (totalChemicals.Volume > 0)
+ if (!_solutionContainer.TryGetSolution(reclaimer, reclaimerComponent.SolutionContainerId, out var outputSolution) ||
+ !_solutionContainer.TryTransferSolution(outputSolution.Value, totalChemicals, totalChemicals.Volume) ||
+ totalChemicals.Volume > 0)
{
_puddle.TrySpillAt(reclaimer, totalChemicals, out _, sound, transformComponent: xform);
}
using Content.Server.Materials;
using Content.Server.Power.Components;
using Content.Shared.Conveyor;
+using Content.Shared.Destructible;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Physics.Controllers;
UpdatesAfter.Add(typeof(MoverController));
SubscribeLocalEvent<ConveyorComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<ConveyorComponent, ComponentShutdown>(OnConveyorShutdown);
-
+ SubscribeLocalEvent<ConveyorComponent, BreakageEventArgs>(OnBreakage);
SubscribeLocalEvent<ConveyorComponent, SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<ConveyorComponent, PowerChangedEvent>(OnPowerChanged);
_fixtures.DestroyFixture(uid, ConveyorFixture, body: physics);
}
+ private void OnBreakage(Entity<ConveyorComponent> ent, ref BreakageEventArgs args)
+ {
+ SetState(ent, ConveyorState.Off, ent);
+ }
+
private void OnPowerChanged(EntityUid uid, ConveyorComponent component, ref PowerChangedEvent args)
{
component.Powered = args.Powered;
if (!Resolve(uid, ref component))
return;
+ if (!_materialReclaimer.SetReclaimerEnabled(uid, state != ConveyorState.Off))
+ return;
+
component.State = state;
if (TryComp<PhysicsComponent>(uid, out var physics))
_broadphase.RegenerateContacts(uid, physics);
- _materialReclaimer.SetReclaimerEnabled(uid, component.State != ConveyorState.Off);
-
UpdateAppearance(uid, component);
Dirty(uid, component);
}
("target", uid),
("tool", args.Used!));
_popup.PopupEntity(str, uid, args.User);
+
+ var ev = new RepairedEvent((uid, component), args.User);
+ RaiseLocalEvent(uid, ref ev);
}
public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args)
args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, new RepairFinishedEvent(), component.FuelCost);
}
}
+
+ /// <summary>
+ /// Event raised on an entity when its successfully repaired.
+ /// </summary>
+ /// <param name="Ent"></param>
+ /// <param name="User"></param>
+ [ByRefEvent]
+ public readonly record struct RepairedEvent(Entity<RepairableComponent> Ent, EntityUid User);
+
}
using Content.Shared.Whitelist;
+using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public bool Enabled = true;
+ /// <summary>
+ /// A master control for whether or not the recycler is broken and can function.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool Broken;
+
/// <summary>
/// How efficiently the materials are reclaimed.
/// In practice, a multiplier per material when calculating the output of the reclaimer.
/// <summary>
/// The id of our output solution
/// </summary>
- [DataField, ViewVariables(VVAccess.ReadWrite)]
- public string SolutionContainerId = "output";
+ [DataField]
+ public string? SolutionContainerId;
/// <summary>
/// a whitelist for what entities can be inserted into this reclaimer
[NetSerializable, Serializable]
public enum RecyclerVisuals
{
- Bloody
+ Bloody,
+ Broken
}
+[UsedImplicitly]
public enum RecyclerVisualLayers : byte
{
- Main,
- Bloody
+ Main
}
using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
using Content.Shared.Body.Components;
-using Content.Shared.Coordinates;
using Content.Shared.Database;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.Stacks;
using Content.Shared.Whitelist;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Map;
if (user != null)
{
- _adminLog.Add(LogType.Action, LogImpact.High,
+ _adminLog.Add(LogType.Action,
+ LogImpact.High,
$"{ToPrettyString(user.Value):player} destroyed {ToPrettyString(item)} in the material reclaimer, {ToPrettyString(uid)}");
}
/// <summary>
/// Sets the Enabled field on the reclaimer.
/// </summary>
- public void SetReclaimerEnabled(EntityUid uid, bool enabled, MaterialReclaimerComponent? component = null)
+ public bool SetReclaimerEnabled(EntityUid uid, bool enabled, MaterialReclaimerComponent? component = null)
{
if (!Resolve(uid, ref component, false))
- return;
+ return true;
+
+ if (component.Broken && enabled)
+ return false;
+
component.Enabled = enabled;
AmbientSound.SetAmbience(uid, enabled && component.Powered);
Dirty(uid, component);
+
+ return true;
}
/// <summary>
if (HasComp<ActiveMaterialReclaimerComponent>(uid))
return false;
- return component.Powered && component.Enabled;
+ return component.Powered && component.Enabled && !component.Broken;
}
/// <summary>
{
return component.Powered &&
component.Enabled &&
+ !component.Broken &&
HasComp<BodyComponent>(victim) &&
HasComp<EmaggedComponent>(uid);
}
Manipulator: 1
Steel: 1
-- type: entity
- id: MaterialReclaimerMachineCircuitboard
- parent: BaseMachineCircuitboard
- name: material reclaimer machine board
- components:
- - type: Sprite
- state: supply
- - type: MachineBoard
- prototype: MaterialReclaimer
- stackRequirements:
- Manipulator: 2
- Steel: 5
- Plastic: 5
-
- type: entity
id: OreProcessorMachineCircuitboard
parent: BaseMachineCircuitboard
- AutolatheMachineCircuitboard
- CircuitImprinterMachineCircuitboard
- OreProcessorMachineCircuitboard
- - MaterialReclaimerMachineCircuitboard
- ElectrolysisUnitMachineCircuitboard
- CentrifugeMachineCircuitboard
- ChemDispenserMachineCircuitboard
+++ /dev/null
-- type: entity
- parent: [ BaseMachinePowered, ConstructibleMachine ]
- id: MaterialReclaimer
- name: material reclaimer
- description: Cannot reclaim immaterial things, like motivation.
- components:
- - type: Sprite
- sprite: Structures/Machines/material_reclaimer.rsi
- snapCardinals: true
- layers:
- - state: icon
- map: ["enum.LatheVisualLayers.IsRunning"]
- - state: gear-active
- map: ["enum.DamageStateVisualLayers.Base"]
- - state: unlit
- shader: unshaded
- map: ["enum.PowerDeviceVisualLayers.Powered"]
- - state: fill-6
- map: ["enum.SolutionContainerLayers.Fill"]
- visible: false
- - state: panel
- map: ["enum.WiresVisualLayers.MaintenancePanel"]
- - type: Appearance
- - type: SolutionContainerVisuals
- maxFillLevels: 6
- fillBaseName: fill-
- - type: WiresVisuals
- - type: GenericVisualizer
- visuals:
- enum.PowerDeviceVisuals.Powered:
- enum.DamageStateVisualLayers.Base:
- True: { state: gear-active}
- False: { state: gear-idle }
- enum.PowerDeviceVisualLayers.Powered:
- True: { visible: true }
- False: { visible: false }
- - type: LitOnPowered
- - type: PointLight
- radius: 1.5
- energy: 1.6
- enabled: false
- color: "#da824d"
- mask: /Textures/Effects/LightMasks/cone.png
- autoRot: true
- offset: "0, 0.4"
- castShadows: false
- - type: PowerSwitch
- - type: Destructible
- thresholds:
- - trigger:
- !type:DamageTrigger
- damage: 100
- behaviors:
- - !type:PlaySoundBehavior
- sound:
- collection: MetalGlassBreak
- - !type:ChangeConstructionNodeBehavior
- node: machineFrame
- - !type:DoActsBehavior
- acts: ["Destruction"]
- - type: Machine
- board: MaterialReclaimerMachineCircuitboard
- - type: WiresPanel
- - type: MaterialReclaimer
- whitelist:
- components:
- - PhysicalComposition
- - SpaceGarbage
- tags:
- - Trash
- - Recyclable
- blacklist:
- components:
- - Material
- - Pda
- - IdCard
- - Brain
- tags:
- - HighRiskItem
- soundCooldown: 0
- sound:
- path: /Audio/Ambience/Objects/crushing.ogg
- params:
- volume: 5
- maxDistance: 5
- loop: true
- - type: MaterialStorage
- insertOnInteract: false
- - type: ContainerContainer
- containers:
- active-material-reclaimer-container: !type:Container
- machine_board: !type:Container
- machine_parts: !type:Container
- - type: SolutionContainerManager
- solutions:
- output:
- maxVol: 100
- - type: DrainableSolution
- solution: output
- - type: ExaminableSolution
- solution: output
- - type: StaticPrice
- price: 500
layers:
- state: grinder-o0
map: ["enum.RecyclerVisualLayers.Main"]
- - state: grinder-o0bld
- map: ["enum.RecyclerVisualLayers.Bloody"]
- visible: false
- type: Appearance
- - type: GenericVisualizer
- visuals:
- enum.RecyclerVisuals.Bloody:
- enum.RecyclerVisualLayers.Main:
- True: { visible: false }
- False: { visible: true }
- enum.RecyclerVisualLayers.Bloody:
- True: { visible: true }
- False: { visible: false }
- enum.ConveyorVisuals.State:
- enum.RecyclerVisualLayers.Main:
- Forward: { state: grinder-o1 }
- Reverse: { state: grinder-o1 }
- Off: { state: grinder-o0 }
- enum.RecyclerVisualLayers.Bloody:
- Forward: { state: grinder-o1bld }
- Reverse: { state: grinder-o1bld }
- Off: { state: grinder-o0bld }
+ - type: RecyclerVisuals
+
- type: CollideMaterialReclaimer
- type: MaterialReclaimer
enabled: false
- efficiency: 0.25
scaleProcessSpeed: false #instant!
minimumProcessDuration: 0
whitelist:
params:
volume: -3
cutOffSound: false
- - type: SolutionContainerManager
- solutions:
- output:
- maxVol: 0 #exists only for the overflow stuff on material reclaimer
- type: MaterialStorage
- type: Conveyor
- type: Rotatable
+ - type: Repairable
+ doAfterDelay: 5
+ fuelCost: 25
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 300
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Breakage"]
+ - !type:PlaySoundBehavior
+ sound:
+ collection: MetalBreak
- type: InteractionPopup
successChance: 1.0
interactSuccessString: petting-success-recycler
interactFailureString: petting-failure-generic
interactSuccessSound:
- path: /Audio/Items/drill_hit.ogg
\ No newline at end of file
+ path: /Audio/Items/drill_hit.ogg
Steel: 100
Glass: 500
-- type: latheRecipe
- id: MaterialReclaimerMachineCircuitboard
- result: MaterialReclaimerMachineCircuitboard
- category: Circuitry
- completetime: 4
- materials:
- Steel: 100
- Glass: 500
-
- type: latheRecipe
id: OreProcessorMachineCircuitboard
result: OreProcessorMachineCircuitboard
Steel: 100
Glass: 500
Gold: 100
-
+
- type: latheRecipe
id: JukeboxCircuitBoard
result: JukeboxCircuitBoard
+++ /dev/null
-{
- "version": 1,
- "license": "CC0-1.0",
- "copyright": "Created by EmoGarbage404 (github) for Space Station 14",
- "size": {
- "x": 32,
- "y": 32
- },
- "states": [
- {
- "name": "fill-1"
- },
- {
- "name": "fill-2"
- },
- {
- "name": "fill-3"
- },
- {
- "name": "fill-4"
- },
- {
- "name": "fill-5"
- },
- {
- "name": "fill-6"
- },
- {
- "name": "gear-active",
- "delays": [
- [
- 0.25,
- 0.25,
- 0.25
- ]
- ]
- },
- {
- "name": "gear-idle"
- },
- {
- "name": "icon"
- },
- {
- "name": "panel"
- },
- {
- "name": "unlit",
- "delays": [
- [
- 0.1,
- 1.0
- ]
- ]
- }
- ]
-}
]
},
{
- "name": "grinder-o0"
-
+ "name": "grinder-o0",
+ "directions": 4
},
{
- "name": "grinder-o0bld"
-
+ "name": "grinder-o0bld",
+ "directions": 4
},
{
"name": "grinder-o1",
+ "directions": 4,
"delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
[
0.1,
0.1,
},
{
"name": "grinder-o1bld",
+ "directions": 4,
"delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
[
0.1,
0.1,
]
]
},
+ {
+ "name": "grinder-o2",
+ "directions": 4
+ },
+ {
+ "name": "grinder-o2bld",
+ "directions": 4
+ },
{
"name": "separator-"
# 2024-07-27
LogicGate: LogicGateOr
+# 2024-08-08
+MaterialReclaimer: null
+MaterialReclaimerMachineCircuitboard: null
+
# 2024-08-11
FoodTacoBeef: FoodTacoShell
FoodTacoChicken: FoodTacoShell
FoodMeatSnakeKebab: FoodKebabSkewer
FoodMeatHawaiianKebab: FoodKebabSkewer
FoodMeatKebab: FoodKebabSkewer
-FoodMeatFiestaKebab: FoodKebabSkewer
\ No newline at end of file
+FoodMeatFiestaKebab: FoodKebabSkewer