Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Combat hypospray #2701

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Content.Server/Chemistry/EntitySystems/HypospraySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteract

public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
{
if (args.Handled) // DeltaV
return;

if (!args.HitEntities.Any())
return;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Server._DV.Chemistry.Components;

/// <summary>
/// For some reason if you set HyposprayComponent onlyAffectsMobs to true it would be able to draw from containers
/// even if injectOnly is also true. I don't want to modify HypospraySystem, so I made this component.
/// - Original Goob Author Aviu00
/// </summary>
[RegisterComponent]
public sealed partial class HyposprayBlockNonMobInjectionComponent : Component
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components;
using Content.Shared.Weapons.Melee.Events;
using Content.Server._DV.Chemistry.Components;

namespace Content.Server._DV.Chemistry.Systems;

public sealed class HyposprayBlockNonMobInjectionSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<HyposprayBlockNonMobInjectionComponent, AfterInteractEvent>(OnAfterInteract, before: new []{typeof(HypospraySystem)});
SubscribeLocalEvent<HyposprayBlockNonMobInjectionComponent, MeleeHitEvent>(OnAttack, before: new []{typeof(HypospraySystem)});
SubscribeLocalEvent<HyposprayBlockNonMobInjectionComponent, UseInHandEvent>(OnUseInHand, before: new []{typeof(HypospraySystem)});
}

private void OnUseInHand(Entity<HyposprayBlockNonMobInjectionComponent> ent, ref UseInHandEvent args)
{
if (!IsMob(args.User))
args.Handled = true;
}

private void OnAttack(Entity<HyposprayBlockNonMobInjectionComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count == 0 || !IsMob(args.HitEntities[0]))
args.Handled = true;
}

private void OnAfterInteract(Entity<HyposprayBlockNonMobInjectionComponent> ent, ref AfterInteractEvent args)
{
if (args.Target == null || !IsMob(args.Target.Value))
args.Handled = true;
}

private bool IsMob(EntityUid uid)
{
return HasComp<MobStateComponent>(uid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Content.Server._DV.Chemistry.Components;

[RegisterComponent]
public sealed partial class BlockDrinkingComponent : Component
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Content.Server._DV.Chemistry.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Nutrition.EntitySystems;

namespace Content.Server._DV.Nutrition.EntitySystems;

public sealed class BlockDrinkingSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<BlockDrinkingComponent, UseInHandEvent>(OnUse, before: [typeof(SharedDrinkSystem)]);
SubscribeLocalEvent<BlockDrinkingComponent, AfterInteractEvent>(AfterInteract,
before: [typeof(SharedDrinkSystem)]);
}

private void OnUse(Entity<BlockDrinkingComponent> entity, ref UseInHandEvent args)
{
args.Handled = true;
}

private void AfterInteract(Entity<BlockDrinkingComponent> entity, ref AfterInteractEvent args)
{
args.Handled = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;

namespace Content.Shared._DV.Chemistry.Components;

[RegisterComponent]
public sealed partial class CartridgeFabricatorComponent : Component
{
[DataField]
public List<string> GroupWhitelist = [];

[DataField]
public List<string> ReagentWhitelist = [];

[DataField]
public string InputSolution = "drink";

[DataField]
public string OutputSolution = "cartridge";

public bool Emagged = false;

[DataField]
public SoundSpecifier SuccessSound = new SoundPathSpecifier("/Audio/Machines/tray_eject.ogg");

[DataField]
public SoundSpecifier FailureSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;

namespace Content.Shared._DV.Chemistry.Components;

[RegisterComponent, NetworkedComponent]
public sealed partial class SolutionCartridgeReceiverComponent : Component
{
public const string CartridgeSlotId = "cartridge-slot";

[DataField("cartridgeSlot")]
public ItemSlot CartridgeSlot = new();

[DataField]
public string HypospraySolution = "hypospray";

[DataField]
public string CartridgeSolution = "cartridge";

[DataField]
public FixedPoint2 MaximumVolume = FixedPoint2.New(30);

[DataField]
public SoundSpecifier InsertSound = new SoundPathSpecifier("/Audio/Weapons/Guns/MagIn/revolver_magin.ogg");
}
144 changes: 144 additions & 0 deletions Content.Shared/_DV/Chemistry/Systems/CartridgeFabricatorSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using System.Linq;
using Content.Shared._DV.Chemistry.Components;
using Content.Shared.Power.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Emag.Systems;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Labels.Components;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Network;
using Content.Shared.Labels.EntitySystems;

namespace Content.Shared._DV.Chemistry.Systems;

public sealed class CartridgeFabricatorSystem : EntitySystem
{
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedSolutionContainerSystem _container = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly TagSystem _tags = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedPowerReceiverSystem _power = default!;
[Dependency] private readonly SharedLabelSystem _labels = default!;
private static readonly ProtoId<TagPrototype>[] BottleTags = ["Bottle"];

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<CartridgeFabricatorComponent, InteractUsingEvent>(OnInteractUsing);

SubscribeLocalEvent<CartridgeFabricatorComponent, OnAttemptEmagEvent>(OnAttemptEmag);
SubscribeLocalEvent<CartridgeFabricatorComponent, GotEmaggedEvent>(OnEmagged);
}

private bool ContainsDisallowedReagents(CartridgeFabricatorComponent fab, Solution solution)
{
foreach (ReagentQuantity reagent in solution.Contents)
{
if (!_prototype.TryIndex<ReagentPrototype>(reagent.Reagent.Prototype, out var prototype) ||
prototype is null)
continue; // TODO: This seems like an error case

if (!fab.GroupWhitelist.Contains(prototype.Group) &&
!fab.ReagentWhitelist.Contains(prototype.ID))
{
return true;
}
}

return false;
}

private void OnInteractUsing(Entity<CartridgeFabricatorComponent> entity, ref InteractUsingEvent args)
{
if (args.Handled || !_power.IsPowered(entity.Owner))
return;

if (!TryComp(args.Used, out SolutionContainerManagerComponent? manager))
return;

if (!_tags.HasAnyTag(args.Used, BottleTags))
return;

if (!_container.TryGetSolution((args.Used, manager),
entity.Comp.InputSolution,
out var _,
out var fromSolution))
return;

// This looks to be something we can handle
args.Handled = true;

if (fromSolution.Volume == 0)
{
_popup.PopupClient(Loc.GetString("cartridge-fabricator-empty-input"), args.User, PopupType.Medium);
_audio.PlayPredicted(entity.Comp.FailureSound, entity.Owner, args.User);
return;
}

// Emagging allows users to make cartridges out of anything
if (!entity.Comp.Emagged &&
ContainsDisallowedReagents(entity.Comp, fromSolution))
{
_popup.PopupClient(Loc.GetString("cartridge-fabricator-denied"), args.User, PopupType.Medium);
_audio.PlayPredicted(entity.Comp.FailureSound, entity.Owner, args.User);
return;
}

if (_net.IsServer)
{
var coords = Transform(entity.Owner).Coordinates;
var cartridge = Spawn("BaseEmptyHypoCartridge", coords);

var cartridgeManager = EnsureComp<SolutionContainerManagerComponent>(cartridge);
if (!_container.TryGetSolution((cartridge, cartridgeManager),
entity.Comp.OutputSolution,
out var cartridgeSolutionEnt,
out var _))
return; // Something very wrong here?

if (!_container.TryAddSolution(cartridgeSolutionEnt.Value, fromSolution))
return;

if (TryComp<LabelComponent>(args.Used, out var bottleLabel))
{
// Propagate the label from the bottle onto the cartridge
_labels.Label(cartridge, bottleLabel.CurrentLabel);
}

QueueDel(args.Used); // Ensure the bottle is gone
_hands.TryPickupAnyHand(args.User, cartridge);
}

_popup.PopupClient(Loc.GetString("cartridge-fabricator-success", ("amount", fromSolution.Volume)),
args.User,
PopupType.Medium);
_audio.PlayPredicted(entity.Comp.SuccessSound, entity.Owner, args.User);
}

private void OnAttemptEmag(Entity<CartridgeFabricatorComponent> entity, ref OnAttemptEmagEvent args)
{
if (entity.Comp.Emagged)
{
// No point in raising more local events when we're already emagged
args.Handled = true;
return;
}
}

private void OnEmagged(Entity<CartridgeFabricatorComponent> entity, ref GotEmaggedEvent args)
{
entity.Comp.Emagged = true;
args.Handled = true;
}
}
Loading
Loading