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

Acidifying Sec Crates & Emergency Lasers #2672

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Content.Shared.Damage;
using Content.Shared.Tools;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;

namespace Content.Server.Containers.AntiTamper;

/// <summary>
/// When a locked container with this component is destroyed, it will
/// acidify the contents.
/// </summary>
[RegisterComponent]
[Access(typeof(AntiTamperSystem))]
public sealed partial class AntiTamperComponent : Component
{
/// <summary>
/// List of containers to acidify. If null,
/// all containers will acidify.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public HashSet<string>? Containers;

/// <summary>
/// The popup message to display when the anti-tamper module
/// is triggered.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public LocId Message = "anti-tamper-contents-destroyed";

/// <summary>
/// The popup message to display when the anti-tamper module
/// fails to trigger.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public LocId FailureMessage = "anti-tamper-random-failure";

/// <summary>
/// The sound to play when the anti-tamper module is triggered.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Items/soda_spray.ogg");

/// <summary>
/// If true, mobs with a <seealso cref="MindContainerComponent"/> will not be
/// deleted, and instead will take <seealso cref="MobDamage"/>.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public bool PreventRoundRemoval = true;

/// <summary>
/// If <seealso cref="PreventRoundRemoval"/> is <c>true</c>, mobs caught inside
/// of the container when the anti-tamper is activated will receive this
/// damage instead of being deleted.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier MobDamage = new()
{
DamageDict = new()
{
{ "Caustic", 85 }
},
};

/// <summary>
/// If true, mobs with
/// <seealso cref="Content.Shared.Interaction.Components.ComplexInteractionComponent">ComplexInteractionComponent</seealso>
/// will be able to disarm the anti-tamper component the crate is open or they are inside of it.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public bool CanDisarm = true;

/// <summary>
/// The length of time it takes to disarm the anti-tamper module. Multiplied by
/// <seealso cref="DisarmLockedMultiplier"/> if the disarming mob is locked
/// inside of the container.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan DisarmTime = TimeSpan.FromSeconds(5);

/// <summary>
/// If the disarming mob is locked inside of the container,
/// the <seealso cref="DisarmTime"/> will be multiplied by this.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float DisarmLockedMultiplier = 4;

/// <summary>
/// The tool required to disarm the anti-tamper module. If null,
/// no tool is required.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public ProtoId<ToolQualityPrototype>? DisarmToolRequired = "Screwing";
}
161 changes: 161 additions & 0 deletions Content.Server/_Impstation/Containers/AntiTamper/AntiTamperSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Containers.AntiTamper;
using Content.Shared.Damage;
using Content.Shared.Destructible;
using Content.Shared.DoAfter;
using Content.Shared.Lock;
using Content.Shared.Mind.Components;
using Content.Shared.Popups;
using Content.Shared.Storage.EntitySystems;
using Content.Shared.Tools.Systems;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Random;

namespace Content.Server.Containers.AntiTamper;

public sealed partial class AntiTamperSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly LockSystem _lockSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly SharedEntityStorageSystem _entityStorageSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;

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

SubscribeLocalEvent<AntiTamperComponent, DestructionEventArgs>(OnDestroy, before: [typeof(EntityStorageSystem)]);
SubscribeLocalEvent<AntiTamperComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
SubscribeLocalEvent<AntiTamperComponent, AntiTamperDisarmDoAfterEvent>(OnDisarmDoAfter);
}

private void OnDestroy(EntityUid uid, AntiTamperComponent comp, DestructionEventArgs args)
{
if (!TryComp<ContainerManagerComponent>(uid, out var containerManager))
return;

if (!_lockSystem.IsLocked(uid))
return;

var coords = Transform(uid).Coordinates;

if (_random.Prob(0.25f))
{
_popupSystem.PopupCoordinates(Loc.GetString(comp.FailureMessage, ("container", uid)), coords, PopupType.Small);
return;
}

foreach (var container in _containerSystem.GetAllContainers(uid, containerManager))
{
if (comp.Containers != null && !comp.Containers.Contains(container.ID))
continue;

foreach (var ent in container.ContainedEntities)
{
if (comp.PreventRoundRemoval && HasComp<MindContainerComponent>(ent))
{
_damageableSystem.TryChangeDamage(ent, comp.MobDamage);
}
else
{
QueueDel(ent);
}
}
}

_popupSystem.PopupCoordinates(Loc.GetString(comp.Message, ("container", uid)), coords, PopupType.SmallCaution);
_audioSystem.PlayPvs(comp.Sound, coords);
}

private void OnGetVerbs(EntityUid uid, AntiTamperComponent comp, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
return;

if (!CanDisarm((uid, comp), args.User, args.Using))
return;

AlternativeVerb verb = new()
{
Text = Loc.GetString("anti-tamper-disarm-verb"),
Act = () =>
{
AttemptDisarm((uid, comp), args.User, args.Using);
}
};
args.Verbs.Add(verb);
}

private bool CanDisarm(Entity<AntiTamperComponent> ent, EntityUid user, EntityUid? used)
{
var uid = ent.Owner;
var comp = ent.Comp;

if (comp.DisarmToolRequired != null && (used == null || !_toolSystem.HasQuality(used.Value, comp.DisarmToolRequired)))
return false;

// Check if crate is unlocked+open or if the player is inside
if (!_containerSystem.ContainsEntity(uid, user))
{
// Disarming entity is outside of the crate
// Crate must be unlocked+open

if (!TryComp<EntityStorageComponent>(uid, out var entStorage))
return false;
if (_lockSystem.IsLocked(uid) || !entStorage.Open)
return false;
}

return true;
}

private void AttemptDisarm(Entity<AntiTamperComponent> ent, EntityUid user, EntityUid? used)
{
if (!CanDisarm(ent, user, used))
return;

var delay = ent.Comp.DisarmTime;
if (_lockSystem.IsLocked(ent.Owner))
delay *= ent.Comp.DisarmLockedMultiplier;

if (ent.Comp.DisarmToolRequired != null)
_toolSystem.UseTool(
used!.Value,
user,
ent.Owner,
delay,
[ent.Comp.DisarmToolRequired.Value],
new AntiTamperDisarmDoAfterEvent(),
out _
);
else
_doAfterSystem.TryStartDoAfter(
new DoAfterArgs(EntityManager, user, delay, new AntiTamperDisarmDoAfterEvent(), ent, ent, used)
{
BreakOnDamage = true,
BreakOnMove = true,
BreakOnDropItem = true,
MovementThreshold = 1.0f,
BlockDuplicate = true,
DuplicateCondition = DuplicateConditions.SameTarget | DuplicateConditions.SameEvent,
}
);
}

private void OnDisarmDoAfter(EntityUid uid, AntiTamperComponent comp, AntiTamperDisarmDoAfterEvent args)
{
if (args.Cancelled || args.Handled || args.Args.Target == null || args.Args.Used == null)
return;

RemComp<AntiTamperComponent>(uid);
_popupSystem.PopupEntity(Loc.GetString("anti-tamper-disarmed", ("container", uid)), uid, args.User);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

namespace Content.Shared.Containers.AntiTamper;

[Serializable, NetSerializable]
public sealed partial class AntiTamperDisarmDoAfterEvent : SimpleDoAfterEvent
{
}
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/_Impstation/anti-tamper.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
anti-tamper-contents-destroyed = The anti-tamper system acidifies the contents of {THE($container)}!
anti-tamper-random-failure = The anti-tamper system failed to acidify the contents of {THE($container)}...
anti-tamper-disarm-verb = Disarm Anti-Tamper
anti-tamper-disarmed = You successfully disarm {THE($container)}'s anti-tamper module.
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,5 @@
- Energy
reflectProb: 0.2
spread: 90
- type: AntiTamper # DeltaV - port impstation's anti-tamper
containers: [ entity_storage ] # DeltaV - port impstation's anti-tamper
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#- !type:DepartmentTimeRequirement
# department: Logistics # DeltaV - Logistics Department replacing Cargo
# time: 21600 # 6 hrs ~3 shifts.
antagAdvantage: 3 # DeltaV - Reduced TC: free shuttle, ez salvage shit, free guns
antagAdvantage: 2 # DeltaV - Reduced TC: free shuttle, ez salvage shit, semi-free guns
# End DeltaV modifications
startingGear: CargoTechGear
icon: "JobIconCargoTechnician"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- type: cargoProduct
id: EmergencyLasers
icon:
sprite: _Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi
state: icon
product: EmergencyLaserCrate
cost: 5000
category: cargoproduct-category-name-emergency
group: market
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- type: entity
id: EmergencyLaserCrate
parent: CrateCommandSecure
name: emergency lasers crate
description: A crate of four emergency laser rifles, for situations where getting clearance isn't an option.
components:
- type: StorageFill
contents:
- id: EmergencyWeaponLaserCarbine
- id: EmergencyWeaponLaserCarbine
- id: EmergencyWeaponLaserCarbine
- id: EmergencyWeaponLaserCarbine
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
- type: entity
name: emergency laser rifle
parent: [WeaponLaserCarbinePractice, BaseGunWieldable, BaseRestrictedContraband]
id: EmergencyWeaponLaserCarbine
description: An emergency laser rifle, for use when getting timely clearance isn't an option. Hand this in to security once the coast is clear.
components:
- type: Sprite
sprite: _Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi
layers:
- state: base
map: ["enum.GunVisualLayers.Base"]
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
- type: StaticPrice
price: 420
- type: HitscanBatteryAmmoProvider
proto: EmergencyRedLaser
fireCost: 50
- type: Item
size: Ginormous
- type: PointLight
radius: 1.5
energy: 14
color: "#ff0043"
mask: /Textures/Effects/LightMasks/double_cone.png
- type: RotatingLight
speed: 180
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- type: hitscan
id: EmergencyRedLaser
damage:
types:
Heat: 12
muzzleFlash:
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
state: muzzle_laser
travelFlash:
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
state: beam
impactFlash:
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
state: impact_laser
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading