Add explosive cord. (#25875)

This commit is contained in:
IProduceWidgets
2025-09-25 17:33:28 -04:00
committed by GitHub
parent 5e55974c0e
commit 80947b128a
54 changed files with 601 additions and 24 deletions

View File

@@ -0,0 +1,14 @@
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
{
}

View File

@@ -1,12 +1,6 @@
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;
}

View File

@@ -447,6 +447,7 @@ namespace Content.Server.NodeContainer.EntitySystems
NodeGroupID.Pipe => Color.Blue,
NodeGroupID.WireNet => Color.DarkMagenta,
NodeGroupID.Teg => Color.Red,
NodeGroupID.ExCable => Color.Pink,
_ => Color.White
};
}

View File

@@ -1,16 +1,35 @@
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;
}
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.Database;
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;
@@ -13,6 +14,7 @@ public sealed partial class CableSystem
[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()
{
@@ -35,12 +37,14 @@ public sealed partial class CableSystem
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;
}

View File

@@ -1,19 +1,11 @@
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
@@ -24,7 +16,6 @@ public sealed partial class StorageSystem : SharedStorageSystem
{
base.Initialize();
SubscribeLocalEvent<StorageComponent, BeforeExplodeEvent>(OnExploded);
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
}

View File

@@ -16,4 +16,5 @@ public enum NodeGroupID : byte
/// <seealso cref="Content.Server.Power.Generation.Teg.TegSystem"/>
/// <seealso cref="Content.Server.Power.Generation.Teg.TegNodeGroup"/>
Teg,
ExCable,
}

View File

@@ -34,6 +34,6 @@ public struct PowerCableChunk
public PowerCableChunk(Vector2i origin)
{
Origin = origin;
PowerCableData = new int[3];
PowerCableData = new int[Enum.GetNames(typeof(CableType)).Length];
}
}

View File

@@ -31,6 +31,7 @@ namespace Content.Shared.Power
HighVoltage,
MediumVoltage,
Apc,
ExCable
}
[Serializable, NetSerializable]

View File

@@ -36,6 +36,10 @@ stack-hv-cable = {$amount ->
[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

View File

@@ -135,3 +135,24 @@
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

View File

@@ -17,7 +17,7 @@
sprite: Objects/Devices/timer.rsi
state: timer
- type: Item
size: Small
size: Tiny
- type: StaticPrice
price: 40
- type: PayloadTrigger
@@ -79,6 +79,8 @@
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

View File

@@ -0,0 +1,115 @@
# 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

View File

@@ -0,0 +1,77 @@
- 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

View File

@@ -1,6 +1,6 @@
- type: entity
abstract: true
id: CableBase
id: CablePhysBase
placement:
mode: SnapgridCenter
components:
@@ -13,11 +13,21 @@
- 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
@@ -33,8 +43,6 @@
- !type:DoActsBehavior
acts: ["Destruction"]
- type: SubFloorHide
- type: CollideOnAnchor
- type: Appearance
- type: Electrified
onHandInteract: false
onInteractUsing: false # wire-cutting handled separately.
@@ -215,3 +223,72 @@
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

View File

@@ -0,0 +1,27 @@
- 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

View File

@@ -0,0 +1,59 @@
- 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

View File

@@ -90,6 +90,8 @@
- ScienceExplosives # sec gets everything for modular grenade making that sci does
id: SecurityExplosives
recipes:
- CableDetStack1
- EmptyDetonator
- ExplosivePayload
- GrenadeBlast
- GrenadeEMP

View File

@@ -216,3 +216,23 @@
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

View File

@@ -116,6 +116,8 @@
- FlashPayload
- ExplosivePayload
- ChemicalPayload
- CableDetStack1
- EmptyDetonator
- type: technology
id: SpecialMeans

View File

@@ -18,3 +18,13 @@
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

View File

@@ -551,6 +551,9 @@
- type: Tag
id: Enzyme
- type: Tag
id: ExCable
- type: Tag
id: ExplosivePassable

View File

@@ -1,7 +1,7 @@
{
"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
@@ -197,6 +197,9 @@
{
"name": "vials"
},
{
"name": "trigger"
},
{
"name": "envelope"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -31,6 +31,14 @@
"name": "coillv-inhand-right",
"directions": 4
},
{
"name": "coilex-inhand-left",
"directions": 4
},
{
"name": "coilex-inhand-right",
"directions": 4
},
{
"name": "coil-30"
},
@@ -70,6 +78,15 @@
{
"name": "coilall-30"
},
{
"name": "coilex-30"
},
{
"name": "coilex-20"
},
{
"name": "coilex-10"
},
{
"name": "coil-equipped-BELT",
"directions": 4
@@ -85,6 +102,10 @@
{
"name": "coilhv-equipped-BELT",
"directions": 4
},
{
"name": "coilex-equipped-BELT",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -0,0 +1,34 @@
{
"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 ]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

View File

@@ -0,0 +1,75 @@
{
"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"
}
]
}