--- /dev/null
+using Content.Server.NodeContainer.NodeGroups;
+using Content.Shared.NodeContainer.NodeGroups;
+using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
+
+namespace Content.Server.ExCable;
+
+/// <summary>
+/// Dummy Node group class for handling the explosive cables.
+/// </summary>
+[NodeGroup(NodeGroupID.ExCable)]
+public sealed class ExCableNodeGroup : BaseNodeGroup
+{
+}
using Content.Shared.Interaction;
-using Content.Shared.Storage;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Containers;
-using Robust.Shared.Player;
namespace Content.Server.Interaction
{
- // TODO Remove Shared prefix
public sealed class InteractionSystem : SharedInteractionSystem;
}
NodeGroupID.Pipe => Color.Blue,
NodeGroupID.WireNet => Color.DarkMagenta,
NodeGroupID.Teg => Color.Red,
+ NodeGroupID.ExCable => Color.Pink,
_ => Color.White
};
}
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Content.Shared.Power;
+using Content.Shared.Whitelist;
namespace Content.Server.Power.Components
{
[RegisterComponent]
public sealed partial class CablePlacerComponent : Component
{
+ /// <summary>
+ /// The structure prototype for the cable coil to place.
+ /// </summary>
[DataField("cablePrototypeID", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? CablePrototypeId = "CableHV";
+ /// <summary>
+ /// What kind of wire prevents placing this wire over it as CableType.
+ /// </summary>
[DataField("blockingWireType")]
public CableType BlockingCableType = CableType.HighVoltage;
+
+ /// <summary>
+ /// Blacklist for things the cable cannot be placed over. For things that arent cables with CableTypes.
+ /// </summary>
+ [DataField]
+ public EntityWhitelist Blacklist = new();
+
+ /// <summary>
+ /// Whether the placed cable should go over tiles or not.
+ /// </summary>
+ [DataField]
+ public bool OverTile;
}
}
using Content.Shared.Interaction;
using Content.Shared.Maps;
using Content.Shared.Stacks;
+using Content.Shared.Whitelist;
using Robust.Shared.Map.Components;
namespace Content.Server.Power.EntitySystems;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
+ [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
private void InitializeCablePlacer()
{
var snapPos = _map.TileIndicesFor((gridUid, grid), args.ClickLocation);
var tileDef = (ContentTileDefinition)_tileManager[_map.GetTileRef(gridUid, grid, snapPos).Tile.TypeId];
- if (!tileDef.IsSubFloor || !tileDef.Sturdy)
+ if ((!component.OverTile && !tileDef.IsSubFloor) || !tileDef.Sturdy)
return;
-
foreach (var anchored in _map.GetAnchoredEntities((gridUid, grid), snapPos))
{
+ if (_whitelistSystem.IsBlacklistPass(component.Blacklist, anchored))
+ return;
+
if (TryComp<CableComponent>(anchored, out var wire) && wire.CableType == component.BlockingCableType)
return;
}
-using Content.Server.Administration.Managers;
-using Content.Shared.Administration;
using Content.Shared.Explosion;
-using Content.Shared.Ghost;
using Content.Shared.Hands;
-using Content.Shared.Lock;
using Content.Shared.Storage;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
-using Content.Shared.Verbs;
-using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-
namespace Content.Server.Storage.EntitySystems;
public sealed partial class StorageSystem : SharedStorageSystem
{
base.Initialize();
SubscribeLocalEvent<StorageComponent, BeforeExplodeEvent>(OnExploded);
-
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
}
/// <seealso cref="Content.Server.Power.Generation.Teg.TegSystem"/>
/// <seealso cref="Content.Server.Power.Generation.Teg.TegNodeGroup"/>
Teg,
+ ExCable,
}
public PowerCableChunk(Vector2i origin)
{
Origin = origin;
- PowerCableData = new int[3];
+ PowerCableData = new int[Enum.GetNames(typeof(CableType)).Length];
}
}
HighVoltage,
MediumVoltage,
Apc,
+ ExCable
}
[Serializable, NetSerializable]
[1] hv cable
*[other] hv cables
}
+stack-explosive-cord = {$amount ->
+ [1] explosive cord
+ *[other] explosive cords
+}
stack-wood-plank = {$amount ->
[1] wood plank
*[other] wood planks
layers:
- state: box_security
- state: forensic
+
+- type: entity
+ parent: BoxCardboard
+ id: BoxDetonator
+ name: detonator box
+ description: A box of explosive detonators and triggers.
+ components:
+ - type: Item
+ shape:
+ - 0,0,1,1
+ - type: StorageFill
+ contents:
+ - id: EmptyDetonator
+ amount: 3
+ - id: TimerTrigger
+ amount: 2
+ - id: VoiceTrigger
+ - type: Sprite
+ layers:
+ - state: box_security
+ - state: trigger
sprite: Objects/Devices/timer.rsi
state: timer
- type: Item
- size: Small
+ size: Tiny
- type: StaticPrice
price: 40
- type: PayloadTrigger
name: voice trigger
description: Adds a machine link that is triggered by vocal keywords.
components:
+ - type: Item
+ size: Tiny
- type: Sprite
sprite: Objects/Devices/voice.rsi
state: voice
--- /dev/null
+# Explosive cable below
+- type: entity
+ parent: [ CableStack, BaseSecurityContraband ]
+ id: CableDetStack
+ name: explosive cord
+ suffix: Full
+ description: Explosive cord for removing whatever is in your way.
+ components:
+ - type: Stack
+ stackType: CableDet
+ baseLayer: base
+ layerStates:
+ - coilex-10
+ - coilex-20
+ - coilex-30
+ - type: Sprite
+ state: coilex-30
+ layers:
+ - state: coilex-30
+ map: ["base"]
+ - type: Item
+ heldPrefix: coilex
+ - type: CablePlacer
+ cablePrototypeID: CableDet
+ blockingWireType: ExCable
+ blacklist:
+ tags:
+ - ExCable
+ overTile: true
+ - type: Appearance
+ - type: Extractable
+ grindableSolutionName: excable
+ - type: SolutionContainerManager
+ solutions:
+ mvcable:
+ reagents:
+ - ReagentId: Thermite
+ Quantity: 3
+ - ReagentId: Charcoal
+ Quantity: 2
+ - type: Damageable
+ damageContainer: StructuralInorganic
+ - type: Destructible # should have the same general explosive behavior as in cables.yml & detonator.yml
+ thresholds:
+ - trigger:
+ !type:DamageTypeTrigger
+ damageType: Structural # as close as we can get to only letting explosives trigger it.
+ damage: 120
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - !type:ExplodeBehavior
+ - trigger:
+ !type:DamageTrigger # the idea here is to prevent you from just beating it until it explodes.
+ damage: 50
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - type: Explosive
+ explosionType: DemolitionCharge
+ totalIntensity: 60
+ intensitySlope: 5
+ maxIntensity: 30
+ canCreateVacuum: false
+ - type: Tag
+ tags:
+ - Payload
+ # - type: Sticky #While cool, this doesn't actually work because the structure prevents the explosion from the cable reaching the stickied wire.
+ # stickDelay: 5
+ # unstickDelay: 5
+ # whitelist:
+ # components:
+ # - Airlock
+ # tags:
+ # - Window
+ # - Wall
+ # - type: StickyVisualizer
+
+- type: entity
+ parent: CableDetStack
+ id: CableDetStack10
+ suffix: 10
+ components:
+ - type: Sprite
+ state: coilex-10
+ - type: Stack
+ count: 10
+ - type: Explosive # TODO: some how make stacking logic handle the explosion scaling. Maybe also something for lingering stacks.
+ explosionType: DemolitionCharge
+ totalIntensity: 30
+ intensitySlope: 5
+ maxIntensity: 15
+ canCreateVacuum: false
+ - type: Tag
+ tags:
+ - Payload
+
+- type: entity
+ parent: CableDetStack
+ id: CableDetStack1
+ suffix: 1
+ components:
+ - type: Sprite
+ state: coilex-10
+ - type: Stack
+ count: 1
+ - type: Explosive
+ explosionType: DemolitionCharge
+ totalIntensity: 10
+ intensitySlope: 5
+ maxIntensity: 5
+ canCreateVacuum: false
+ - type: Tag
+ tags:
+ - Payload
--- /dev/null
+- type: entity
+ parent: [ BaseItem, BaseSecurityContraband ]
+ id: EmptyDetonator
+ name: detonator cap
+ description: A detonator cap. Requires a trigger and wire.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Bombs/detonator.rsi
+ layers:
+ - state: empty
+ map: [ "enum.ConstructionVisuals.Layer" ]
+ - type: Item
+ size: Small
+ - type: PayloadCase
+ - type: Construction
+ graph: DetonatorGraph
+ node: emptyDetonator
+ - type: Damageable
+ damageContainer: StructuralInorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTypeTrigger
+ damageType: Structural # as close as we can get to only letting explosives trigger it.
+ damage: 120
+ behaviors:
+ - !type:TriggerBehavior
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - trigger:
+ !type:DamageTrigger # the idea here is to prevent you from just beating it until it explodes.
+ damage: 50
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.ConstructionVisuals.Key:
+ enum.ConstructionVisuals.Layer:
+ emptyDetonator: { state: empty }
+ detonatorWithTrigger: { state: addtrigger }
+ wiredDetonator: { state: complete }
+ enum.Trigger.TriggerVisuals.VisualState:
+ enum.ConstructionVisuals.Layer:
+ Primed: { state: primed }
+ Unprimed: { state: complete }
+ - type: StaticPrice
+ price: 25
+
+- type: entity
+ parent: EmptyDetonator
+ id: WiredDetonator
+ name: detonator cap
+ description: A detonator cap.
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Explosive # this is the cord, but its a little smaller because I dont want these to be grenades. They need to not really break walls themselves, but do enough to set off the cable. ~125 structural.
+ explosionType: DemolitionCharge
+ totalIntensity: 2.5
+ intensitySlope: 100
+ maxIntensity: 2.5
+ canCreateVacuum: false
+ - type: ExplodeOnTrigger
+ - type: Sticky
+ stickDelay: 2
+ unstickDelay: 2
+ stickPopupStart: comp-sticky-start-stick-bomb
+ stickPopupSuccess: comp-sticky-success-stick-bomb
+ unstickPopupStart: comp-sticky-start-unstick-bomb
+ unstickPopupSuccess: comp-sticky-success-unstick-bomb
+ whitelist:
+ tags:
+ - ExCable
+ blacklist: # can't stick it to other items
+ components:
+ - Item
- type: entity
abstract: true
- id: CableBase
+ id: CablePhysBase
placement:
mode: SnapgridCenter
components:
- type: Transform
anchored: true
noRot: true
- # TODO: Remove both of these, same with CollideOnAnchor
+ # TODO: Remove both of these, same with CollideOnAnchor
- type: Physics
bodyType: Static
canCollide: false
- type: Fixtures
+ - type: CollideOnAnchor
+ - type: Appearance
+
+- type: entity
+ abstract: true
+ parent: CablePhysBase
+ id: CableBase
+ components:
+ - type: Visibility
+ layer: 1
- type: Sprite
drawdepth: ThinWire
visible: false
- !type:DoActsBehavior
acts: ["Destruction"]
- type: SubFloorHide
- - type: CollideOnAnchor
- - type: Appearance
- type: Electrified
onHandInteract: false
onInteractUsing: false # wire-cutting handled separately.
components:
- type: Cable
cuttingQuality: null
+
+# Explosive cable below
+- type: entity
+ id: CableDet
+ parent: [ CablePhysBase, BaseSecurityContraband ]
+ name: explosive cord
+ description: Spaghetti for people who hate walls.
+ components:
+ - type: Cable
+ cableDroppedOnCutPrototype: CableDetStack1
+ cableType: ExCable
+ - type: Sprite
+# color: white # maybe change this later and move the stripes to the CableVisualizerComp layer instead
+ sprite: Structures/Power/Cables/ex_cable.rsi
+ state: excable_0
+ - type: Icon
+ sprite: Structures/Power/Cables/ex_cable.rsi
+ state: excable_4
+ - type: Damageable
+ damageContainer: StructuralInorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTypeTrigger
+ damageType: Structural #this is as close as we can get to only letting explosives set it off.
+ damage: 120
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - !type:ExplodeBehavior
+ - trigger:
+ !type:DamageTrigger #A fallback that deletes it but doesnt explode in case something somehow gets here without exploding violently enough.
+ damage: 50
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - trigger:
+ !type:DamageTrigger
+ damage: 35
+ behaviors:
+ - !type:SpawnEntitiesBehavior
+ spawn:
+ CableDetStack1:
+ min: 1
+ max: 1
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - type: CableVis
+ node: cordage
+ - type: NodeContainer
+ nodes:
+ cordage:
+ !type:CableNode
+ nodeGroupID: ExCable
+ - type: CableVisualizer
+ statePrefix: excable_
+ - type: Explosive
+ explosionType: DemolitionCharge
+ totalIntensity: 10
+ intensitySlope: 5
+ maxIntensity: 5
+ canCreateVacuum: false
+ - type: ExplodeOnTrigger
+ - type: Construction
+ graph: DetCable
+ node: detonationCable
+ - type: Tag
+ tags:
+ - ExCable
--- /dev/null
+- type: constructionGraph
+ id: DetCable
+ start: start
+ graph:
+ - node: start
+ edges:
+ - to: detonationCable
+ completed:
+ - !type:SnapToGrid
+ southRotation: true
+ steps:
+ - material: Bananium
+ amount: 1
+ doAfter: 0
+
+ - node: detonationCable
+ entity: CableDet
+ edges:
+ - to: start
+ completed:
+ - !type:SpawnPrototype
+ prototype: CableDetStack1
+ amount: 1
+ - !type:DeleteEntity {}
+ steps:
+ - tool: Cutting
+ doAfter: 0.5
--- /dev/null
+- type: constructionGraph
+ id: DetonatorGraph
+ start: start
+ graph:
+ - node: start
+ edges:
+ - to: emptyDetonator
+ steps:
+ - material: Bananium
+ amount: 1
+ doAfter: 1
+
+ - node: emptyDetonator
+ entity: EmptyDetonator
+ actions:
+ - !type:AppearanceChange
+ edges:
+ - to: detonatorWithTrigger
+ steps:
+ - component: PayloadTrigger
+ store: payloadTrigger
+ name: construction-graph-component-payload-trigger
+ doAfter: 0.5
+
+ - node: detonatorWithTrigger
+ entity: EmptyDetonator
+ actions:
+ - !type:AppearanceChange
+ - !type:PlaySound
+ sound: /Audio/Machines/button.ogg
+ edges:
+ - to: emptyDetonator
+ steps:
+ - tool: Prying
+ doAfter: 0.5
+ completed:
+ - !type:EmptyContainer
+ container: payloadTrigger
+ - to: wiredDetonator
+ steps:
+ - material: CableDet
+ doAfter: 0.5
+
+ - node: wiredDetonator
+ entity: WiredDetonator
+ actions:
+ - !type:AppearanceChange
+ - !type:PlaySound
+ sound: /Audio/Machines/button.ogg
+ - !type:AdminLog
+ message: "A detonator cap was crafted"
+ edges:
+ - to: detonatorWithTrigger
+ steps:
+ - tool: Cutting
+ doAfter: 0.5
+ completed:
+ - !type:SpawnPrototype
+ prototype: CableDetStack1
- ScienceExplosives # sec gets everything for modular grenade making that sci does
id: SecurityExplosives
recipes:
+ - CableDetStack1
+ - EmptyDetonator
- ExplosivePayload
- GrenadeBlast
- GrenadeEMP
Steel: 500
Glass: 400
Gold: 100
+
+- type: latheRecipe
+ id: CableDetStack1
+ result: CableDetStack1
+ categories:
+ - Weapons
+ completetime: 2
+ materials:
+ Plastic: 50
+ Plasma: 25
+ Gold: 20
+
+- type: latheRecipe
+ id: EmptyDetonator
+ result: EmptyDetonator
+ categories:
+ - Weapons
+ completetime: 3
+ materials:
+ Steel: 100
- FlashPayload
- ExplosivePayload
- ChemicalPayload
+ - CableDetStack1
+ - EmptyDetonator
- type: technology
id: SpecialMeans
icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilhv-30 }
spawn: CableHVStack1
maxCount: 30
+
+# Explosive cable below
+
+- type: stack
+ id: CableDet
+ name: stack-explosive-cord
+ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilex-30 }
+ spawn: CableDetStack1
+ maxCount: 30
+
- type: Tag
id: Enzyme
+- type: Tag
+ id: ExCable
+
- type: Tag
id: ExplosivePassable
{
"version": 1,
"license": "CC-BY-SA-3.0",
- "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/ca674eff9d23e04357b7609ef7e07eadfc1a993f and modified by Flareguy (github), encryptokey was taken from Baystation12 at https://github.com/infinitystation/Baystation12/blob/073f678cdce92edb8fcd55f9ffc9f0523bf31506/icons/obj/radio.dmi and modified by lapatison. boxwidetoy, shelltoy, swab, flare, inflatable, trashbag, magazine, holo and forensic created by potato1234x (github) for ss14 based on toys.rsi, mouth_swab.rsi, flare.rsi, inflatable_wall.rsi, trashbag.rsi, caseless_pistol_mag.rsi, guardians.rsi and bureaucracy.rsi respectively, candle and darts created by TheShuEd for ss14, throwing_knives and vials was drawn by Ubaser, evidence_markers by moomoobeef, nitrogentank modified from extendedtank by Errant, agrichemkit by Cerol, modified by ps3moira (github). sechud renamed to secglasses, new sechud, sunglasses by K-Dynamic (github). utensils by gentleman-bird (github)",
+ "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/ca674eff9d23e04357b7609ef7e07eadfc1a993f and modified by Flareguy (github), encryptokey was taken from Baystation12 at https://github.com/infinitystation/Baystation12/blob/073f678cdce92edb8fcd55f9ffc9f0523bf31506/icons/obj/radio.dmi and modified by lapatison. boxwidetoy, shelltoy, swab, flare, inflatable, trashbag, magazine, holo and forensic created by potato1234x (github) for ss14 based on toys.rsi, mouth_swab.rsi, flare.rsi, inflatable_wall.rsi, trashbag.rsi, caseless_pistol_mag.rsi, guardians.rsi and bureaucracy.rsi respectively, candle and darts created by TheShuEd for ss14, throwing_knives and vials was drawn by Ubaser, evidence_markers by moomoobeef, nitrogentank modified from extendedtank by Errant, agrichemkit by Cerol, modified by ps3moira (github). sechud renamed to secglasses, new sechud, sunglasses by K-Dynamic (github), trigger by IProduceWidgets, utensils by gentleman-bird (github).",
"size": {
"x": 32,
"y": 32
{
"name": "vials"
},
+ {
+ "name": "trigger"
+ },
{
"name": "envelope"
},
"name": "coillv-inhand-right",
"directions": 4
},
+ {
+ "name": "coilex-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "coilex-inhand-right",
+ "directions": 4
+ },
{
"name": "coil-30"
},
{
"name": "coilall-30"
},
+ {
+ "name": "coilex-30"
+ },
+ {
+ "name": "coilex-20"
+ },
+ {
+ "name": "coilex-10"
+ },
{
"name": "coil-equipped-BELT",
"directions": 4
{
"name": "coilhv-equipped-BELT",
"directions": 4
+ },
+ {
+ "name": "coilex-equipped-BELT",
+ "directions": 4
}
]
}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from austation at commit https://github.com/austation/austation/commit/71d8e7406d84f8ec8cb79bf153b050e7e09d2a17 and modified by IProduceWidgets",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "empty",
+ "directions": 1
+ },
+ {
+ "name": "wired",
+ "directions": 1
+ },
+ {
+ "name": "addtrigger",
+ "directions": 1
+ },
+ {
+ "name": "complete",
+ "directions": 1
+ },
+ {
+ "name": "primed",
+ "directions": 1,
+ "delays": [
+ [ 0.2, 0.2 ]
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/fcf375d7d9ce6ceed5c7face899725e5655ab640, striping added by IProduceWidgets",
+ "states": [
+ {
+ "name": "excable_0"
+
+ },
+ {
+ "name": "excable_1"
+
+ },
+ {
+ "name": "excable_2"
+
+ },
+ {
+ "name": "excable_3"
+
+ },
+ {
+ "name": "excable_4"
+
+ },
+ {
+ "name": "excable_5"
+
+ },
+ {
+ "name": "excable_6"
+
+ },
+ {
+ "name": "excable_7"
+
+ },
+ {
+ "name": "excable_8"
+
+ },
+ {
+ "name": "excable_9"
+
+ },
+ {
+ "name": "excable_10"
+
+ },
+ {
+ "name": "excable_11"
+
+ },
+ {
+ "name": "excable_12"
+
+ },
+ {
+ "name": "excable_13"
+
+ },
+ {
+ "name": "excable_14"
+
+ },
+ {
+ "name": "excable_15"
+
+ }
+ ]
+}