From 62f5a31c4abcaaca09af84f591cbe727e6fcb7cb Mon Sep 17 00:00:00 2001
From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Date: Tue, 22 Oct 2024 15:03:42 +0200
Subject: [PATCH] Syringe gun! (#32112)
* Init testing
* copyright
* oops
* Tracking the embed entity uid
* testing stuff for gradual injection
* work
* weh
* god save me
* bleh
* Yippee!
* Again
* Mini syringe ammo
* cleaning up
* mini syringes have a texture for fill amount
* -3 cool points :(
* hitboxes
* init cleanup
* much needed fixes
* Fixes
---
.../SolutionInjectWhileEmbeddedComponent.cs | 24 +++++++
.../SolutionInjectOnEventSystem.cs | 7 ++
.../SolutionInjectWhileEmbeddedSystem.cs | 60 ++++++++++++++++++
.../Chemistry/InjectOverTimeEvent.cs | 13 ++++
.../EmbeddableProjectileComponent.cs | 18 ++++--
.../Projectiles/SharedProjectileSystem.cs | 4 ++
.../Weapons/Guns/Gunshots/attributions.yml | 7 +-
.../Weapons/Guns/Gunshots/syringe_gun.ogg | Bin 0 -> 9476 bytes
.../Entities/Objects/Specific/chemistry.yml | 60 ++++++++++++++++++
.../Objects/Weapons/Guns/pneumatic_cannon.yml | 37 +++++++++++
Resources/Prototypes/tags.yml | 3 +
.../Specific/Chemistry/syringe.rsi/meta.json | 9 +++
.../Chemistry/syringe.rsi/minisyringe1.png | Bin 0 -> 5346 bytes
.../Chemistry/syringe.rsi/minisyringe2.png | Bin 0 -> 5371 bytes
.../Chemistry/syringe.rsi/minisyringe3.png | Bin 0 -> 5378 bytes
.../Chemistry/syringe.rsi/syringeproj.png | Bin 257 -> 5589 bytes
.../Cannons/syringe_gun.rsi/inhand-left.png | Bin 0 -> 337 bytes
.../Cannons/syringe_gun.rsi/inhand-right.png | Bin 0 -> 341 bytes
.../Guns/Cannons/syringe_gun.rsi/meta.json | 22 +++++++
.../Cannons/syringe_gun.rsi/syringe_gun.png | Bin 0 -> 446 bytes
20 files changed, 257 insertions(+), 7 deletions(-)
create mode 100644 Content.Server/Chemistry/Components/SolutionInjectWhileEmbeddedComponent.cs
create mode 100644 Content.Server/Chemistry/EntitySystems/SolutionInjectWhileEmbeddedSystem.cs
create mode 100644 Content.Shared/Chemistry/InjectOverTimeEvent.cs
create mode 100644 Resources/Audio/Weapons/Guns/Gunshots/syringe_gun.ogg
create mode 100644 Resources/Textures/Objects/Specific/Chemistry/syringe.rsi/minisyringe1.png
create mode 100644 Resources/Textures/Objects/Specific/Chemistry/syringe.rsi/minisyringe2.png
create mode 100644 Resources/Textures/Objects/Specific/Chemistry/syringe.rsi/minisyringe3.png
create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/syringe_gun.rsi/inhand-left.png
create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/syringe_gun.rsi/inhand-right.png
create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/syringe_gun.rsi/meta.json
create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/syringe_gun.rsi/syringe_gun.png
diff --git a/Content.Server/Chemistry/Components/SolutionInjectWhileEmbeddedComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectWhileEmbeddedComponent.cs
new file mode 100644
index 0000000000..0f10e2a449
--- /dev/null
+++ b/Content.Server/Chemistry/Components/SolutionInjectWhileEmbeddedComponent.cs
@@ -0,0 +1,24 @@
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+
+namespace Content.Server.Chemistry.Components;
+
+///
+/// Used for embeddable entities that should try to inject a
+/// contained solution into a target over time while they are embbeded into.
+///
+[RegisterComponent, AutoGenerateComponentPause]
+public sealed partial class SolutionInjectWhileEmbeddedComponent : BaseSolutionInjectOnEventComponent {
+ ///
+ ///The time at which the injection will happen.
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
+ public TimeSpan NextUpdate;
+
+ ///
+ ///The delay between each injection in seconds.
+ ///
+ [DataField]
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3);
+}
+
diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs
index d56fded024..f15edcf067 100644
--- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs
@@ -2,6 +2,7 @@ using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Events;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Content.Shared.Projectiles;
@@ -29,6 +30,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
SubscribeLocalEvent(HandleProjectileHit);
SubscribeLocalEvent(HandleEmbed);
SubscribeLocalEvent(HandleMeleeHit);
+ SubscribeLocalEvent(OnInjectOverTime);
}
private void HandleProjectileHit(Entity entity, ref ProjectileHitEvent args)
@@ -49,6 +51,11 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
TryInjectTargets((entity.Owner, entity.Comp), args.HitEntities, args.User);
}
+ private void OnInjectOverTime(Entity entity, ref InjectOverTimeEvent args)
+ {
+ DoInjection((entity.Owner, entity.Comp), args.EmbeddedIntoUid);
+ }
+
private void DoInjection(Entity injectorEntity, EntityUid target, EntityUid? source = null)
{
TryInjectTargets(injectorEntity, [target], source);
diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectWhileEmbeddedSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectWhileEmbeddedSystem.cs
new file mode 100644
index 0000000000..2baeba9da1
--- /dev/null
+++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectWhileEmbeddedSystem.cs
@@ -0,0 +1,60 @@
+using Content.Server.Body.Components;
+using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Events;
+using Content.Shared.Inventory;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Tag;
+using Content.Shared.Weapons.Melee.Events;
+using Robust.Shared.Collections;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Chemistry.EntitySystems;
+
+///
+/// System for handling injecting into an entity while a projectile is embedded.
+///
+public sealed class SolutionInjectWhileEmbeddedSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly BloodstreamSystem _bloodstream = default!;
+ [Dependency] private readonly InventorySystem _inventory = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
+ [Dependency] private readonly TagSystem _tag = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnMapInit);
+ }
+
+ private void OnMapInit(Entity ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var injectComponent, out var projectileComponent))
+ {
+ if (_gameTiming.CurTime < injectComponent.NextUpdate)
+ continue;
+
+ injectComponent.NextUpdate += injectComponent.UpdateInterval;
+
+ if(projectileComponent.EmbeddedIntoUid == null)
+ continue;
+
+ var ev = new InjectOverTimeEvent(projectileComponent.EmbeddedIntoUid.Value);
+ RaiseLocalEvent(uid, ref ev);
+
+ }
+ }
+}
diff --git a/Content.Shared/Chemistry/InjectOverTimeEvent.cs b/Content.Shared/Chemistry/InjectOverTimeEvent.cs
new file mode 100644
index 0000000000..ca5ab4213f
--- /dev/null
+++ b/Content.Shared/Chemistry/InjectOverTimeEvent.cs
@@ -0,0 +1,13 @@
+namespace Content.Shared.Chemistry.Events;
+
+///
+/// Raised directed on an entity when it embeds in another entity.
+///
+[ByRefEvent]
+public readonly record struct InjectOverTimeEvent(EntityUid embeddedIntoUid)
+{
+ ///
+ /// Entity that is embedded in.
+ ///
+ public readonly EntityUid EmbeddedIntoUid = embeddedIntoUid;
+}
diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
index 008b7c2ced..e4125945d2 100644
--- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
+++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs
@@ -13,37 +13,43 @@ public sealed partial class EmbeddableProjectileComponent : Component
///
/// Minimum speed of the projectile to embed.
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public float MinimumSpeed = 5f;
///
/// Delete the entity on embedded removal?
/// Does nothing if there's no RemovalTime.
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public bool DeleteOnRemove;
///
/// How long it takes to remove the embedded object.
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public float? RemovalTime = 3f;
///
/// Whether this entity will embed when thrown, or only when shot as a projectile.
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public bool EmbedOnThrow = true;
///
/// How far into the entity should we offset (0 is wherever we collided).
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public Vector2 Offset = Vector2.Zero;
///
/// Sound to play after embedding into a hit target.
///
- [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public SoundSpecifier? Sound;
+
+ ///
+ /// Uid of the entity the projectile is embed into.
+ ///
+ [DataField, AutoNetworkedField]
+ public EntityUid? EmbeddedIntoUid;
}
diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs
index 1b7d6d6f99..85e75d6d29 100644
--- a/Content.Shared/Projectiles/SharedProjectileSystem.cs
+++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs
@@ -71,6 +71,8 @@ public abstract partial class SharedProjectileSystem : EntitySystem
TryComp(uid, out var physics);
_physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform);
_transform.AttachToGridOrMap(uid, xform);
+ component.EmbeddedIntoUid = null;
+ Dirty(uid, component);
// Reset whether the projectile has damaged anything if it successfully was removed
if (TryComp(uid, out var projectile))
@@ -127,8 +129,10 @@ public abstract partial class SharedProjectileSystem : EntitySystem
}
_audio.PlayPredicted(component.Sound, uid, null);
+ component.EmbeddedIntoUid = target;
var ev = new EmbedEvent(user, target);
RaiseLocalEvent(uid, ref ev);
+ Dirty(uid, component);
}
private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args)
diff --git a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml
index 7c46974818..89db045c96 100644
--- a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml
+++ b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml
@@ -26,4 +26,9 @@
- files: ["ship_duster.ogg", "ship_friendship.ogg", "ship_svalinn.ogg", "ship_perforator.ogg"]
license: "CC0-1.0"
copyright: "Created by MIXnikita for Space Station 14"
- source: "https://github.com/MIXnikita"
\ No newline at end of file
+ source: "https://github.com/MIXnikita"
+
+- files: ["syringe_gun.ogg"]
+ license: "CC-BY-SA-3.0"
+ copyright: "Taken from vgstation"
+ source: "https://github.com/vgstation-coders/vgstation13/commit/23303188abe6fe31b114a218a1950d7325a23730"
\ No newline at end of file
diff --git a/Resources/Audio/Weapons/Guns/Gunshots/syringe_gun.ogg b/Resources/Audio/Weapons/Guns/Gunshots/syringe_gun.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..2132808ecdc37a11856fbe10774236ffbb5a6954
GIT binary patch
literal 9476
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