diff --git a/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperComponent.cs b/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperComponent.cs new file mode 100644 index 00000000000..aa6b622e617 --- /dev/null +++ b/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperComponent.cs @@ -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; + +/// +/// When a locked container with this component is destroyed, it will +/// acidify the contents. +/// +[RegisterComponent] +[Access(typeof(AntiTamperSystem))] +public sealed partial class AntiTamperComponent : Component +{ + /// + /// List of containers to acidify. If null, + /// all containers will acidify. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public HashSet? Containers; + + /// + /// The popup message to display when the anti-tamper module + /// is triggered. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public LocId Message = "anti-tamper-contents-destroyed"; + + /// + /// The popup message to display when the anti-tamper module + /// fails to trigger. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public LocId FailureMessage = "anti-tamper-random-failure"; + + /// + /// The sound to play when the anti-tamper module is triggered. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Items/soda_spray.ogg"); + + /// + /// If true, mobs with a will not be + /// deleted, and instead will take . + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool PreventRoundRemoval = true; + + /// + /// If is true, mobs caught inside + /// of the container when the anti-tamper is activated will receive this + /// damage instead of being deleted. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier MobDamage = new() + { + DamageDict = new() + { + { "Caustic", 85 } + }, + }; + + /// + /// If true, mobs with + /// ComplexInteractionComponent + /// will be able to disarm the anti-tamper component the crate is open or they are inside of it. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool CanDisarm = true; + + /// + /// The length of time it takes to disarm the anti-tamper module. Multiplied by + /// if the disarming mob is locked + /// inside of the container. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan DisarmTime = TimeSpan.FromSeconds(5); + + /// + /// If the disarming mob is locked inside of the container, + /// the will be multiplied by this. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float DisarmLockedMultiplier = 4; + + /// + /// The tool required to disarm the anti-tamper module. If null, + /// no tool is required. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public ProtoId? DisarmToolRequired = "Screwing"; +} diff --git a/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperSystem.cs b/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperSystem.cs new file mode 100644 index 00000000000..4a1b13834cf --- /dev/null +++ b/Content.Server/_Impstation/Containers/AntiTamper/AntiTamperSystem.cs @@ -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(OnDestroy, before: [typeof(EntityStorageSystem)]); + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnDisarmDoAfter); + } + + private void OnDestroy(EntityUid uid, AntiTamperComponent comp, DestructionEventArgs args) + { + if (!TryComp(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(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 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 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(uid, out var entStorage)) + return false; + if (_lockSystem.IsLocked(uid) || !entStorage.Open) + return false; + } + + return true; + } + + private void AttemptDisarm(Entity 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(uid); + _popupSystem.PopupEntity(Loc.GetString("anti-tamper-disarmed", ("container", uid)), uid, args.User); + } +} diff --git a/Content.Shared/_Impstation/Containers/AntiTamper/AntiTamperDisarmDoAfterEvent.cs b/Content.Shared/_Impstation/Containers/AntiTamper/AntiTamperDisarmDoAfterEvent.cs new file mode 100644 index 00000000000..2c7ff1dd503 --- /dev/null +++ b/Content.Shared/_Impstation/Containers/AntiTamper/AntiTamperDisarmDoAfterEvent.cs @@ -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 +{ +} diff --git a/Resources/Locale/en-US/_Impstation/anti-tamper.ftl b/Resources/Locale/en-US/_Impstation/anti-tamper.ftl new file mode 100644 index 00000000000..9de361650a0 --- /dev/null +++ b/Resources/Locale/en-US/_Impstation/anti-tamper.ftl @@ -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. diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 107e8f94d50..54fccd30b38 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -186,6 +186,8 @@ sprite: Structures/Storage/Crates/sec_gear.rsi - type: AccessReader access: [["Security"]] + - type: AntiTamper # DeltaV - port impstation's anti-tamper + containers: [ entity_storage ] # DeltaV - port impstation's anti-tamper - type: entity parent: CrateBaseSecure @@ -290,6 +292,8 @@ sprite: Structures/Storage/Crates/weapon.rsi - type: AccessReader access: [["Armory"]] + - type: AntiTamper # DeltaV - port impstation's anti-tamper + containers: [ entity_storage ] # DeltaV - port impstation's anti-tamper - type: entity parent: [ CrateBaseSecure, BaseRestrictedContraband ] diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml index 392b2b43cae..6de5e3655a9 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml @@ -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" diff --git a/Resources/Prototypes/_Impstation/Catalog/Cargo/cargo_emergency.yml b/Resources/Prototypes/_Impstation/Catalog/Cargo/cargo_emergency.yml new file mode 100644 index 00000000000..c1bb50a5ca7 --- /dev/null +++ b/Resources/Prototypes/_Impstation/Catalog/Cargo/cargo_emergency.yml @@ -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 diff --git a/Resources/Prototypes/_Impstation/Catalog/Fills/Crates/emergency.yml b/Resources/Prototypes/_Impstation/Catalog/Fills/Crates/emergency.yml new file mode 100644 index 00000000000..12fecffe52a --- /dev/null +++ b/Resources/Prototypes/_Impstation/Catalog/Fills/Crates/emergency.yml @@ -0,0 +1,10 @@ +- type: entity + parent: CrateCommandSecure + id: EmergencyLaserCrate + 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 + amount: 4 diff --git a/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Battery/emergencylaser.yml b/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Battery/emergencylaser.yml new file mode 100644 index 00000000000..cdc19230ea4 --- /dev/null +++ b/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Battery/emergencylaser.yml @@ -0,0 +1,28 @@ +- type: entity + parent: [WeaponLaserCarbinePractice, BaseGunWieldable, BaseRestrictedContraband] + id: EmergencyWeaponLaserCarbine + name: emergency laser rifle + 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 diff --git a/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml new file mode 100644 index 00000000000..a33ba0d9ab6 --- /dev/null +++ b/Resources/Prototypes/_Impstation/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml @@ -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 diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/base.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/base.png new file mode 100644 index 00000000000..a631c503e55 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/base.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-BACKPACK.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-BACKPACK.png new file mode 100644 index 00000000000..af91f7fe615 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-BACKPACK.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-SUITSTORAGE.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-SUITSTORAGE.png new file mode 100644 index 00000000000..af91f7fe615 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/equipped-SUITSTORAGE.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/icon.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/icon.png new file mode 100644 index 00000000000..001b4a09d6c Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/icon.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-left.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-left.png new file mode 100644 index 00000000000..6ed1213c782 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-left.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-right.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-right.png new file mode 100644 index 00000000000..c672c69cf2b Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/inhand-right.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-1.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-1.png new file mode 100644 index 00000000000..e021aae704b Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-1.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-2.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-2.png new file mode 100644 index 00000000000..0a051aae142 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-2.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-3.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-3.png new file mode 100644 index 00000000000..fd7847d16f0 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-3.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-4.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-4.png new file mode 100644 index 00000000000..efa7d9ea526 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/mag-unshaded-4.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/meta.json b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/meta.json new file mode 100644 index 00000000000..2b374ec4dd8 --- /dev/null +++ b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/meta.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Polaris at https://github.com/PolarisSS13/Polaris/commit/9ded73fb85b9106d6bbf1c9a34d1d2fa27ee0e2e, backpack sprite by Peptide, backpack sling sprite edited by Boaz1111, wield sprites by RiceMar1244", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "base" + }, + { + "name": "mag-unshaded-1" + }, + { + "name": "mag-unshaded-2" + }, + { + "name": "mag-unshaded-3" + }, + { + "name": "mag-unshaded-4" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "wielded-inhand-left", + "directions": 4 + }, + { + "name": "wielded-inhand-right", + "directions": 4 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-left.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-left.png new file mode 100644 index 00000000000..5ba3bf5c240 Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-left.png differ diff --git a/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-right.png b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-right.png new file mode 100644 index 00000000000..e9b1892f0ac Binary files /dev/null and b/Resources/Textures/_Impstation/Objects/Weapons/Guns/Battery/emergency_laser_gun.rsi/wielded-inhand-right.png differ