From f499dfb63a73b2ee408c92d2e98c80ca03e4d71a Mon Sep 17 00:00:00 2001 From: TinManTim <73014819+Tin-Man-Tim@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:59:09 -0500 Subject: [PATCH] Adds explosion when attempting to microwave metal / bugfix (#23887) --- .../Components/ActiveMicrowaveComponent.cs | 5 + .../Kitchen/Components/MicrowaveComponent.cs | 23 +++++ .../Kitchen/EntitySystems/MicrowaveSystem.cs | 99 ++++++++++++++----- Content.Server/Lightning/LightningSystem.cs | 22 +++-- .../Destructible/SharedDestructibleSystem.cs | 2 +- .../Prototypes/Entities/Effects/lightning.yml | 27 +++++ .../Structures/Machines/microwave.yml | 7 ++ Resources/Prototypes/tags.yml | 12 +-- 8 files changed, 153 insertions(+), 44 deletions(-) diff --git a/Content.Server/Kitchen/Components/ActiveMicrowaveComponent.cs b/Content.Server/Kitchen/Components/ActiveMicrowaveComponent.cs index da8a35a695..bd5449dde6 100644 --- a/Content.Server/Kitchen/Components/ActiveMicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/ActiveMicrowaveComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Kitchen; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Kitchen.Components; @@ -14,6 +15,10 @@ public sealed partial class ActiveMicrowaveComponent : Component [ViewVariables(VVAccess.ReadWrite)] public float TotalTime; + [ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan MalfunctionTime = TimeSpan.Zero; + [ViewVariables] public (FoodRecipePrototype?, int) PortionedRecipe; } diff --git a/Content.Server/Kitchen/Components/MicrowaveComponent.cs b/Content.Server/Kitchen/Components/MicrowaveComponent.cs index b11370c991..142a98236a 100644 --- a/Content.Server/Kitchen/Components/MicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/MicrowaveComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Construction.Prototypes; using Content.Shared.DeviceLinking; using Content.Shared.Item; using Robust.Shared.Audio; @@ -12,8 +13,10 @@ namespace Content.Server.Kitchen.Components { [DataField("cookTimeMultiplier"), ViewVariables(VVAccess.ReadWrite)] public float CookTimeMultiplier = 1; + [DataField("baseHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)] public float BaseHeatMultiplier = 100; + [DataField("objectHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)] public float ObjectHeatMultiplier = 100; @@ -23,10 +26,13 @@ namespace Content.Server.Kitchen.Components #region audio [DataField("beginCookingSound")] public SoundSpecifier StartCookingSound = new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg"); + [DataField("foodDoneSound")] public SoundSpecifier FoodDoneSound = new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg"); + [DataField("clickSound")] public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); + [DataField("ItemBreakSound")] public SoundSpecifier ItemBreakSound = new SoundPathSpecifier("/Audio/Effects/clang.ogg"); @@ -72,6 +78,23 @@ namespace Content.Server.Kitchen.Components [DataField, ViewVariables(VVAccess.ReadWrite)] public ProtoId MaxItemSize = "Normal"; + + /// + /// How frequently the microwave can malfunction. + /// + [DataField] + public float MalfunctionInterval = 1.0f; + + /// + /// Chance of an explosion occurring when we microwave a metallic object + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float ExplosionChance = .1f; + + /// + /// Chance of lightning occurring when we microwave a metallic object + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float LightningChance = .75f; } public sealed class BeingMicrowavedEvent : HandledEntityEventArgs diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index efa963f2d0..e724a71987 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Body.Systems; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Construction; +using Content.Server.Explosion.EntitySystems; using Content.Server.DeviceLinking.Events; using Content.Server.DeviceLinking.Systems; using Content.Server.Hands.Systems; @@ -18,33 +19,39 @@ using Content.Shared.Destructible; using Content.Shared.FixedPoint; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Robust.Shared.Random; +using Robust.Shared.Audio; +using Content.Server.Lightning; using Content.Shared.Item; using Content.Shared.Kitchen; using Content.Shared.Kitchen.Components; using Content.Shared.Popups; using Content.Shared.Power; using Content.Shared.Tag; -using Robust.Server.Containers; using Robust.Server.GameObjects; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Player; using System.Linq; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Server.Kitchen.EntitySystems { public sealed class MicrowaveSystem : EntitySystem { [Dependency] private readonly BodySystem _bodySystem = default!; - [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly DeviceLinkSystem _deviceLink = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly PowerReceiverSystem _power = default!; [Dependency] private readonly RecipeManager _recipeManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _sharedContainer = default!; + [Dependency] private readonly LightningSystem _lightning = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ExplosionSystem _explosion = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TemperatureSystem _temperature = default!; @@ -52,6 +59,9 @@ namespace Content.Server.Kitchen.EntitySystems [Dependency] private readonly HandsSystem _handsSystem = default!; [Dependency] private readonly SharedItemSystem _item = default!; + [ValidatePrototypeId] + private const string MalfunctionSpark = "Spark"; + public override void Initialize() { base.Initialize(); @@ -76,6 +86,7 @@ namespace Content.Server.Kitchen.EntitySystems SubscribeLocalEvent(OnCookStart); SubscribeLocalEvent(OnCookStop); + SubscribeLocalEvent(OnEntityUnpaused); SubscribeLocalEvent(OnActiveMicrowaveInsert); SubscribeLocalEvent(OnActiveMicrowaveRemove); @@ -96,9 +107,19 @@ namespace Content.Server.Kitchen.EntitySystems { if (!TryComp(ent, out var microwaveComponent)) return; - SetAppearance(ent.Owner, MicrowaveVisualState.Idle, microwaveComponent); + SetAppearance(ent.Owner, MicrowaveVisualState.Idle, microwaveComponent); microwaveComponent.PlayingStream = _audio.Stop(microwaveComponent.PlayingStream); + + foreach (var solid in microwaveComponent.Storage.ContainedEntities) + { + RemComp(solid); + } + } + + private void OnEntityUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.MalfunctionTime += args.PausedTime; } private void OnActiveMicrowaveInsert(Entity ent, ref EntInsertedIntoContainerMessage args) @@ -197,7 +218,7 @@ namespace Content.Server.Kitchen.EntitySystems if (metaData.EntityPrototype.ID == recipeSolid.Key) { - _sharedContainer.Remove(item, component.Storage); + _container.Remove(item, component.Storage); EntityManager.DeleteEntity(item); break; } @@ -312,7 +333,7 @@ namespace Content.Server.Kitchen.EntitySystems ent.Comp.Broken = true; SetAppearance(ent, MicrowaveVisualState.Broken, ent.Comp); RemComp(ent); - _sharedContainer.EmptyContainer(ent.Comp.Storage); + _container.EmptyContainer(ent.Comp.Storage); UpdateUserInterfaceState(ent, ent.Comp); } @@ -328,8 +349,8 @@ namespace Content.Server.Kitchen.EntitySystems private void OnAnchorChanged(EntityUid uid, MicrowaveComponent component, ref AnchorStateChangedEvent args) { - if(!args.Anchored) - _sharedContainer.EmptyContainer(component.Storage); + if (!args.Anchored) + _container.EmptyContainer(component.Storage); } private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args) @@ -370,6 +391,31 @@ namespace Content.Server.Kitchen.EntitySystems return component.Storage.ContainedEntities.Any(); } + /// + /// Handles the attempted cooking of unsafe objects + /// + /// + /// Returns false if the microwave didn't explode, true if it exploded. + /// + private void RollMalfunction(Entity ent) + { + if (ent.Comp1.MalfunctionTime == TimeSpan.Zero) + return; + + if (ent.Comp1.MalfunctionTime > _gameTiming.CurTime) + return; + + ent.Comp1.MalfunctionTime = _gameTiming.CurTime + TimeSpan.FromSeconds(ent.Comp2.MalfunctionInterval); + if (_random.Prob(ent.Comp2.ExplosionChance)) + { + _explosion.TriggerExplosive(ent); + return; // microwave is fucked, stop the cooking. + } + + if (_random.Prob(ent.Comp2.LightningChance)) + _lightning.ShootRandomLightnings(ent, 1.0f, 2, MalfunctionSpark, triggerLightningEvents: false); + } + /// /// Starts Cooking /// @@ -384,9 +430,9 @@ namespace Content.Server.Kitchen.EntitySystems var solidsDict = new Dictionary(); var reagentDict = new Dictionary(); + var malfunctioning = false; // TODO use lists of Reagent quantities instead of reagent prototype ids. - - foreach (var item in component.Storage.ContainedEntities) + foreach (var item in component.Storage.ContainedEntities.ToArray()) { // special behavior when being microwaved ;) var ev = new BeingMicrowavedEvent(uid, user); @@ -398,20 +444,17 @@ namespace Content.Server.Kitchen.EntitySystems return; } - // destroy microwave - if (_tag.HasTag(item, "MicrowaveMachineUnsafe") || _tag.HasTag(item, "Metal")) + if (_tag.HasTag(item, "Metal")) { - component.Broken = true; - SetAppearance(uid, MicrowaveVisualState.Broken, component); - _audio.PlayPvs(component.ItemBreakSound, uid); - return; + malfunctioning = true; } - if (_tag.HasTag(item, "MicrowaveSelfUnsafe") || _tag.HasTag(item, "Plastic")) + if (_tag.HasTag(item, "Plastic")) { var junk = Spawn(component.BadRecipeEntityId, Transform(uid).Coordinates); _container.Insert(junk, component.Storage); - QueueDel(item); + Del(item); + continue; } AddComp(item); @@ -454,6 +497,8 @@ namespace Content.Server.Kitchen.EntitySystems activeComp.CookTimeRemaining = component.CurrentCookTimerTime * component.CookTimeMultiplier; activeComp.TotalTime = component.CurrentCookTimerTime; //this doesn't scale so that we can have the "actual" time activeComp.PortionedRecipe = portionedRecipe; + if (malfunctioning) + activeComp.MalfunctionTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.MalfunctionInterval); UpdateUserInterfaceState(uid, component); } @@ -505,8 +550,12 @@ namespace Content.Server.Kitchen.EntitySystems var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var active, out var microwave)) { - //check if there's still cook time left + active.CookTimeRemaining -= frameTime; + + RollMalfunction((uid, active,microwave)); + + //check if there's still cook time left if (active.CookTimeRemaining > 0) { AddTemperature(microwave, frameTime); @@ -517,7 +566,9 @@ namespace Content.Server.Kitchen.EntitySystems AddTemperature(microwave, Math.Max(frameTime + active.CookTimeRemaining, 0)); //Though there's still a little bit more heat to pump out foreach (var solid in microwave.Storage.ContainedEntities) + { EntityManager.RemoveComponentDeferred(solid); + } if (active.PortionedRecipe.Item1 != null) { @@ -529,10 +580,10 @@ namespace Content.Server.Kitchen.EntitySystems } } - _sharedContainer.EmptyContainer(microwave.Storage); + _container.EmptyContainer(microwave.Storage); UpdateUserInterfaceState(uid, microwave); EntityManager.RemoveComponentDeferred(uid); - _audio.PlayPvs(microwave.FoodDoneSound, uid, AudioParams.Default.WithVolume(-1)); + _audio.PlayPvs(microwave.FoodDoneSound, uid); } } @@ -542,7 +593,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!HasContents(ent.Comp) || HasComp(ent)) return; - _sharedContainer.EmptyContainer(ent.Comp.Storage); + _container.EmptyContainer(ent.Comp.Storage); _audio.PlayPvs(ent.Comp.ClickSound, ent, AudioParams.Default.WithVolume(-2)); UpdateUserInterfaceState(ent, ent.Comp); } @@ -552,7 +603,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!HasContents(ent.Comp) || HasComp(ent)) return; - _sharedContainer.Remove(EntityManager.GetEntity(args.EntityID), ent.Comp.Storage); + _container.Remove(EntityManager.GetEntity(args.EntityID), ent.Comp.Storage); UpdateUserInterfaceState(ent, ent.Comp); } diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 5e975f3179..4f975a60fd 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -3,7 +3,6 @@ using Content.Server.Beam; using Content.Server.Beam.Components; using Content.Server.Lightning.Components; using Content.Shared.Lightning; -using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Lightning; @@ -22,9 +21,6 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; - private List> _lookupTargetsList = new(); - private HashSet> _lookupTargets = new(); - public override void Initialize() { base.Initialize(); @@ -48,15 +44,20 @@ public sealed class LightningSystem : SharedLightningSystem /// Where the lightning fires from /// Where the lightning fires to /// The prototype for the lightning to be created - public void ShootLightning(EntityUid user, EntityUid target, string lightningPrototype = "Lightning") + /// if the lightnings being fired should trigger lightning events. + public void ShootLightning(EntityUid user, EntityUid target, string lightningPrototype = "Lightning", bool triggerLightningEvents = true) { var spriteState = LightningRandomizer(); _beam.TryCreateBeam(user, target, lightningPrototype, spriteState); - var ev = new HitByLightningEvent(user, target); - RaiseLocalEvent(target, ref ev); + if (triggerLightningEvents) // we don't want certain prototypes to trigger lightning level events + { + var ev = new HitByLightningEvent(user, target); + RaiseLocalEvent(target, ref ev); + } } + /// /// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning. /// @@ -65,7 +66,8 @@ public sealed class LightningSystem : SharedLightningSystem /// Number of lightning bolts /// The prototype for the lightning to be created /// how many times to recursively fire lightning bolts from the target points of the first shot. - public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0) + /// if the lightnings being fired should trigger lightning events. + public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true) { //To Do: add support to different priority target tablem for different lightning types //To Do: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent) @@ -88,10 +90,10 @@ public sealed class LightningSystem : SharedLightningSystem if (!_random.Prob(curTarget.HitProbability)) //Chance to ignore target continue; - ShootLightning(user, targets[count].Owner, lightningPrototype); + ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents); if (arcDepth - targets[count].LightningResistance > 0) { - ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance); + ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents); } shootedCount++; } diff --git a/Content.Shared/Destructible/SharedDestructibleSystem.cs b/Content.Shared/Destructible/SharedDestructibleSystem.cs index b16009e198..eb87f00b6c 100644 --- a/Content.Shared/Destructible/SharedDestructibleSystem.cs +++ b/Content.Shared/Destructible/SharedDestructibleSystem.cs @@ -14,7 +14,7 @@ public abstract class SharedDestructibleSystem : EntitySystem } /// - /// Force entity to broke. + /// Force entity to break. /// public void BreakEntity(EntityUid owner) { diff --git a/Resources/Prototypes/Entities/Effects/lightning.yml b/Resources/Prototypes/Entities/Effects/lightning.yml index 62a4153c00..7afd1c07a0 100644 --- a/Resources/Prototypes/Entities/Effects/lightning.yml +++ b/Resources/Prototypes/Entities/Effects/lightning.yml @@ -81,6 +81,33 @@ canArc: true lightningPrototype: ChargedLightning +- type: entity + name: lightning + id: Spark + parent: BaseLightning + noSpawn: true + components: + - type: Sprite + sprite: /Textures/Effects/lightning.rsi + drawdepth: Effects + layers: + - state: "lightning_1" + shader: unshaded + - type: Electrified + shockDamage: 0 + - type: Lightning + lightningPrototype: Spark + - type: PointLight + radius: 0.2 + softness: .4 + - type: Beam + sound: /Audio/Effects/sparks4.ogg + - type: TimedDespawn + lifetime: .3 + - type: Tag + tags: + - HideContextMenu + - type: entity name: supercharged lightning id: SuperchargedLightning diff --git a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml index 16e43494a6..37b5e50d31 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/microwave.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/microwave.yml @@ -83,6 +83,13 @@ behaviors: - !type:DoActsBehavior acts: ["Breakage"] + - type: Explosive + explosionType: Default + maxIntensity: 10 + totalIntensity: 5 + intensitySlope: 5 + canCreateVacuum: false + deleteAfterExplosion: false - type: ApcPowerReceiver powerLoad: 400 - type: Machine diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 40736c3fc5..cf36243d9d 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -565,10 +565,10 @@ - type: Tag id: Goat - + - type: Tag id: GPS - + - type: Tag id: Grenade @@ -797,7 +797,7 @@ id: Matchstick - type: Tag - id: Mayo + id: Mayo - type: Tag id: Meat @@ -808,12 +808,6 @@ - type: Tag id: Metal -- type: Tag - id: MicrowaveMachineUnsafe - -- type: Tag - id: MicrowaveSelfUnsafe - - type: Tag id: MindShield