Pipebombs and IED rework (#25705)

* inital

* 2-9

* add crafting function, rename ied to fire bomb

* add firebomb ignition

* fikss

* change damage values

* add note

* fix tests i think

* ok

* good

* Review

* warning

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Mr. 27
2024-03-14 00:27:08 -04:00
committed by GitHub
parent 2f2cd4aab7
commit 3cb1c585c5
20 changed files with 291 additions and 44 deletions

View File

@@ -17,11 +17,15 @@ using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
namespace Content.Server.Explosion.EntitySystems; namespace Content.Server.Explosion.EntitySystems;
public sealed partial class ExplosionSystem public sealed partial class ExplosionSystem
{ {
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
/// <summary> /// <summary>
/// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>. /// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>.
/// </summary> /// </summary>
@@ -95,7 +99,7 @@ public sealed partial class ExplosionSystem
{ {
// EXPLOSION TODO allow explosion spawning to be interrupted by time limit. In the meantime, ensure that // EXPLOSION TODO allow explosion spawning to be interrupted by time limit. In the meantime, ensure that
// there is at-least 1ms of time left before creating a new explosion // there is at-least 1ms of time left before creating a new explosion
if (MathF.Max(MaxProcessingTime - 1, 0.1f) < Stopwatch.Elapsed.TotalMilliseconds) if (MathF.Max(MaxProcessingTime - 1, 0.1f) < Stopwatch.Elapsed.TotalMilliseconds)
break; break;
if (!_explosionQueue.TryDequeue(out var spawnNextExplosion)) if (!_explosionQueue.TryDequeue(out var spawnNextExplosion))
@@ -130,11 +134,11 @@ public sealed partial class ExplosionSystem
try try
{ {
#endif #endif
var processed = _activeExplosion.Process(tilesRemaining); var processed = _activeExplosion.Process(tilesRemaining);
tilesRemaining -= processed; tilesRemaining -= processed;
// has the explosion finished processing? // has the explosion finished processing?
if (_activeExplosion.FinishedProcessing) if (_activeExplosion.FinishedProcessing)
{ {
var comp = EnsureComp<TimedDespawnComponent>(_activeExplosion.VisualEnt); var comp = EnsureComp<TimedDespawnComponent>(_activeExplosion.VisualEnt);
comp.Lifetime = _cfg.GetCVar(CCVars.ExplosionPersistence); comp.Lifetime = _cfg.GetCVar(CCVars.ExplosionPersistence);
@@ -203,7 +207,8 @@ public sealed partial class ExplosionSystem
DamageSpecifier damage, DamageSpecifier damage,
MapCoordinates epicenter, MapCoordinates epicenter,
HashSet<EntityUid> processed, HashSet<EntityUid> processed,
string id) string id,
float? fireStacks)
{ {
var size = grid.Comp.TileSize; var size = grid.Comp.TileSize;
var gridBox = new Box2(tile * size, (tile + 1) * size); var gridBox = new Box2(tile * size, (tile + 1) * size);
@@ -222,7 +227,7 @@ public sealed partial class ExplosionSystem
// process those entities // process those entities
foreach (var (uid, xform) in list) foreach (var (uid, xform) in list)
{ {
ProcessEntity(uid, epicenter, damage, throwForce, id, xform); ProcessEntity(uid, epicenter, damage, throwForce, id, xform, fireStacks);
} }
// process anchored entities // process anchored entities
@@ -232,7 +237,7 @@ public sealed partial class ExplosionSystem
foreach (var entity in _anchored) foreach (var entity in _anchored)
{ {
processed.Add(entity); processed.Add(entity);
ProcessEntity(entity, epicenter, damage, throwForce, id, null); ProcessEntity(entity, epicenter, damage, throwForce, id, null, fireStacks);
} }
// Walls and reinforced walls will break into girders. These girders will also be considered turf-blocking for // Walls and reinforced walls will break into girders. These girders will also be considered turf-blocking for
@@ -268,7 +273,7 @@ public sealed partial class ExplosionSystem
{ {
// Here we only throw, no dealing damage. Containers n such might drop their entities after being destroyed, but // Here we only throw, no dealing damage. Containers n such might drop their entities after being destroyed, but
// they should handle their own damage pass-through, with their own damage reduction calculation. // they should handle their own damage pass-through, with their own damage reduction calculation.
ProcessEntity(uid, epicenter, null, throwForce, id, xform); ProcessEntity(uid, epicenter, null, throwForce, id, xform, null);
} }
return !tileBlocked; return !tileBlocked;
@@ -303,7 +308,8 @@ public sealed partial class ExplosionSystem
DamageSpecifier damage, DamageSpecifier damage,
MapCoordinates epicenter, MapCoordinates epicenter,
HashSet<EntityUid> processed, HashSet<EntityUid> processed,
string id) string id,
float? fireStacks)
{ {
var gridBox = Box2.FromDimensions(tile * DefaultTileSize, new Vector2(DefaultTileSize, DefaultTileSize)); var gridBox = Box2.FromDimensions(tile * DefaultTileSize, new Vector2(DefaultTileSize, DefaultTileSize));
var worldBox = spaceMatrix.TransformBox(gridBox); var worldBox = spaceMatrix.TransformBox(gridBox);
@@ -319,7 +325,7 @@ public sealed partial class ExplosionSystem
foreach (var (uid, xform) in state.Item1) foreach (var (uid, xform) in state.Item1)
{ {
processed.Add(uid); processed.Add(uid);
ProcessEntity(uid, epicenter, damage, throwForce, id, xform); ProcessEntity(uid, epicenter, damage, throwForce, id, xform, fireStacks);
} }
if (throwForce <= 0) if (throwForce <= 0)
@@ -333,7 +339,7 @@ public sealed partial class ExplosionSystem
foreach (var (uid, xform) in list) foreach (var (uid, xform) in list)
{ {
ProcessEntity(uid, epicenter, null, throwForce, id, xform); ProcessEntity(uid, epicenter, null, throwForce, id, xform, fireStacks);
} }
} }
@@ -430,7 +436,8 @@ public sealed partial class ExplosionSystem
DamageSpecifier? originalDamage, DamageSpecifier? originalDamage,
float throwForce, float throwForce,
string id, string id,
TransformComponent? xform) TransformComponent? xform,
float? fireStacksOnIgnite)
{ {
if (originalDamage != null) if (originalDamage != null)
{ {
@@ -439,6 +446,17 @@ public sealed partial class ExplosionSystem
{ {
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin. // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
_damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true); _damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true);
}
}
// ignite
if (fireStacksOnIgnite != null)
{
if (_flammableQuery.TryGetComponent(uid, out var flammable))
{
flammable.FireStacks += fireStacksOnIgnite.Value;
_flammableSystem.Ignite(uid, uid, flammable);
} }
} }
@@ -705,14 +723,14 @@ sealed class Explosion
{ {
_currentIntensity = _tileSetIntensity[CurrentIteration]; _currentIntensity = _tileSetIntensity[CurrentIteration];
#if DEBUG #if DEBUG
if (_expectedDamage != null) if (_expectedDamage != null)
{ {
// Check that explosion processing hasn't somehow accidentally mutated the damage set. // Check that explosion processing hasn't somehow accidentally mutated the damage set.
DebugTools.Assert(_expectedDamage.Equals(_currentDamage)); DebugTools.Assert(_expectedDamage.Equals(_currentDamage));
_expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity; _expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity;
} }
#endif #endif
_currentDamage = ExplosionType.DamagePerIntensity * _currentIntensity; _currentDamage = ExplosionType.DamagePerIntensity * _currentIntensity;
@@ -811,7 +829,8 @@ sealed class Explosion
_currentDamage, _currentDamage,
Epicenter, Epicenter,
ProcessedEntities, ProcessedEntities,
ExplosionType.ID); ExplosionType.ID,
ExplosionType.FireStacks);
// If the floor is not blocked by some dense object, damage the floor tiles. // If the floor is not blocked by some dense object, damage the floor tiles.
if (canDamageFloor) if (canDamageFloor)
@@ -828,7 +847,8 @@ sealed class Explosion
_currentDamage, _currentDamage,
Epicenter, Epicenter,
ProcessedEntities, ProcessedEntities,
ExplosionType.ID); ExplosionType.ID,
ExplosionType.FireStacks);
} }
if (!MoveNext()) if (!MoveNext())

View File

@@ -52,7 +52,7 @@ public sealed partial class ExplosionSystem : EntitySystem
[Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedMapSystem _map = default!;
private EntityQuery<TransformComponent> _transformQuery; private EntityQuery<TransformComponent> _transformQuery;
private EntityQuery<DamageableComponent> _damageQuery; private EntityQuery<FlammableComponent> _flammableQuery;
private EntityQuery<PhysicsComponent> _physicsQuery; private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<ProjectileComponent> _projectileQuery; private EntityQuery<ProjectileComponent> _projectileQuery;
@@ -104,7 +104,7 @@ public sealed partial class ExplosionSystem : EntitySystem
InitVisuals(); InitVisuals();
_transformQuery = GetEntityQuery<TransformComponent>(); _transformQuery = GetEntityQuery<TransformComponent>();
_damageQuery = GetEntityQuery<DamageableComponent>(); _flammableQuery = GetEntityQuery<FlammableComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>(); _physicsQuery = GetEntityQuery<PhysicsComponent>();
_projectileQuery = GetEntityQuery<ProjectileComponent>(); _projectileQuery = GetEntityQuery<ProjectileComponent>();
} }

View File

@@ -25,6 +25,12 @@ public sealed partial class ExplosionPrototype : IPrototype
[DataField("damagePerIntensity", required: true)] [DataField("damagePerIntensity", required: true)]
public DamageSpecifier DamagePerIntensity = default!; public DamageSpecifier DamagePerIntensity = default!;
/// <summary>
/// Amount of firestacks to apply in addition to igniting.
/// </summary>
[DataField]
public float? FireStacks;
/// <summary> /// <summary>
/// This set of points, together with <see cref="_tileBreakIntensity"/> define a function that maps the /// This set of points, together with <see cref="_tileBreakIntensity"/> define a function that maps the
/// explosion intensity to a tile break chance via linear interpolation. /// explosion intensity to a tile break chance via linear interpolation.

View File

@@ -25,6 +25,7 @@ materials-meat = meat
materials-web = silk materials-web = silk
materials-bones = bone materials-bones = bone
materials-coal = coal materials-coal = coal
materials-gunpowder = gunpowder
# Ores # Ores
materials-raw-iron = raw iron materials-raw-iron = raw iron

View File

@@ -571,3 +571,22 @@
components: components:
- type: Stack - type: Stack
count: 1 count: 1
- type: entity
parent: MaterialBase
id: MaterialGunpowder
name: gunpowder
description: An explosive compound.
components:
- type: Stack
stackType: Gunpowder
count: 1
- type: Sprite
sprite: Objects/Misc/reagent_fillings.rsi
state: powderpile
color: darkgray
- type: PhysicalComposition
materialComposition:
Gunpowder: 100
- type: Item
size: Tiny

View File

@@ -3,8 +3,8 @@
# with that you could make napalm ied instead of welding fuel with no additional complexity # with that you could make napalm ied instead of welding fuel with no additional complexity
- type: entity - type: entity
parent: BaseItem parent: BaseItem
id: ImprovisedExplosive id: FireBomb
name: improvised explosive device name: fire bomb
description: A weak, improvised incendiary device. description: A weak, improvised incendiary device.
components: components:
- type: Sprite - type: Sprite
@@ -26,10 +26,10 @@
volume: 1 volume: 1
- type: RandomTimerTrigger - type: RandomTimerTrigger
min: 0 min: 0
max: 60 max: 15
- type: Explosive # Weak explosion in a very small radius. Doesn't break underplating. - type: Explosive # Weak explosion in a very small radius. Ignites surrounding entities.
explosionType: Default explosionType: FireBomb
totalIntensity: 20 totalIntensity: 25
intensitySlope: 5 intensitySlope: 5
maxIntensity: 3 maxIntensity: 3
canCreateVacuum: false canCreateVacuum: false
@@ -50,14 +50,14 @@
acts: ["Destruction"] acts: ["Destruction"]
- !type:ExplodeBehavior - !type:ExplodeBehavior
- type: Construction - type: Construction
graph: ImprovisedExplosive graph: FireBomb
node: ied node: firebomb
# has igniter but no fuel or wires # has igniter but no fuel or wires
- type: entity - type: entity
parent: DrinkColaCanEmpty parent: DrinkColaCanEmpty
id: ImprovisedExplosiveEmpty id: FireBombEmpty
name: improvised explosive device name: fire bomb
suffix: empty suffix: empty
description: A weak, improvised incendiary device. This one has no fuel. description: A weak, improvised incendiary device. This one has no fuel.
components: components:
@@ -74,17 +74,17 @@
True: {state: "base"} True: {state: "base"}
False: {state: "base"} False: {state: "base"}
- type: Construction - type: Construction
graph: ImprovisedExplosive graph: FireBomb
node: empty node: empty
defaultTarget: ied defaultTarget: firebomb
- type: Tag - type: Tag
tags: tags:
- Trash - Trash
# no DrinkCan, prevent using it to make another ied # no DrinkCan, prevent using it to make another ied
- type: entity - type: entity
parent: ImprovisedExplosiveEmpty parent: FireBombEmpty
id: ImprovisedExplosiveFuel id: FireBombFuel
suffix: fuel suffix: fuel
description: A weak, improvised incendiary device. This one is missing wires. description: A weak, improvised incendiary device. This one is missing wires.
components: components:
@@ -102,6 +102,6 @@
Quantity: 30 Quantity: 30
- type: Construction - type: Construction
node: fuel node: fuel
defaultTarget: ied defaultTarget: firebomb
- type: Tag - type: Tag
tags: [] tags: []

View File

@@ -0,0 +1,62 @@
- type: entity
parent: GrenadeBase
id: PipeBomb
name: pipe bomb
description: An improvised explosive made from pipes and wire.
components:
- type: Sprite
sprite: Objects/Weapons/Bombs/pipebomb.rsi
layers:
- state: base
map: ["enum.TriggerVisualLayers.Base"]
- state: wires
- type: OnUseTimerTrigger # todo: make it activate through welder/lighter/fire instead
delay: 5
examinable: false
initialBeepDelay: 0
beepSound: /Audio/Effects/lightburn.ogg
- type: RandomTimerTrigger
min: 1
max: 10
- type: ExplodeOnTrigger
- type: Explosive # Weak explosion in a very small radius. Doesn't break underplating.
explosionType: Default
totalIntensity: 50
intensitySlope: 5
maxIntensity: 6
canCreateVacuum: false
- type: Appearance
- type: TimerTriggerVisuals
- type: Construction
graph: PipeBomb
node: pipebomb
- type: entity
parent: BaseItem
id: PipeBombGunpowder
name: pipe bomb
description: An improvised explosive made from a pipe. This one has no gunpowder.
suffix: Gunpowder
components:
- type: Sprite
sprite: Objects/Weapons/Bombs/pipebomb.rsi
state: base
- type: Construction
graph: PipeBomb
node: gunpowder
defaultTarget: pipebomb
- type: entity
parent: BaseItem
id: PipeBombCable
name: pipe bomb
description: An improvised explosive made from a pipe. This one has no cable.
suffix: Cable
components:
- type: Sprite
sprite: Objects/Weapons/Bombs/pipebomb.rsi
state: base
- type: Construction
graph: PipeBomb
node: cable
defaultTarget: pipebomb

View File

@@ -116,3 +116,11 @@
icon: { sprite: Objects/Materials/ore.rsi, state: coal } icon: { sprite: Objects/Materials/ore.rsi, state: coal }
color: "#404040" color: "#404040"
price: 0 price: 0
- type: material
id: Gunpowder
name: materials-gunpowder
unit: materials-unit-piece
icon: { sprite: Objects/Misc/reagent_fillings.rsi, state: powderpile }
color: "#A9A9A9"
price: 0

View File

@@ -1,5 +1,5 @@
- type: constructionGraph - type: constructionGraph
id: ImprovisedExplosive id: FireBomb
start: start start: start
graph: graph:
- node: start - node: start
@@ -19,7 +19,7 @@
state: icon state: icon
doAfter: 1 doAfter: 1
- node: empty - node: empty
entity: ImprovisedExplosiveEmpty entity: FireBombEmpty
edges: edges:
- to: start - to: start
completed: completed:
@@ -42,7 +42,7 @@
- tool: Screwing - tool: Screwing
doAfter: 1 doAfter: 1
- node: fuel - node: fuel
entity: ImprovisedExplosiveFuel entity: FireBombFuel
edges: edges:
- to: empty - to: empty
conditions: conditions:
@@ -51,7 +51,7 @@
steps: steps:
- tool: Screwing - tool: Screwing
doAfter: 1 doAfter: 1
- to: ied - to: firebomb
conditions: # no dumping out 29u of the fuel then adding wires :) conditions: # no dumping out 29u of the fuel then adding wires :)
- !type:MinSolution - !type:MinSolution
solution: drink solution: drink
@@ -62,8 +62,8 @@
- material: Cable - material: Cable
amount: 5 amount: 5
doAfter: 2 doAfter: 2
- node: ied - node: firebomb
entity: ImprovisedExplosive entity: FireBomb
edges: edges:
- to: fuel - to: fuel
completed: completed:

View File

@@ -0,0 +1,43 @@
- type: constructionGraph
id: PipeBomb
start: start
graph:
- node: start
edges:
- to: gunpowder
steps:
- tag: Pipe
icon:
sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeStraight
name: pipe
- material: Steel
amount: 1
doAfter: 3
- node: gunpowder
entity: PipeBombGunpowder
edges:
- to: cable
steps:
- material: Gunpowder
amount: 5
doAfter: 3
- node: cable
entity: PipeBombCable
edges:
- to: pipebomb
steps:
- material: Cable
amount: 5
doAfter: 2
- node: pipebomb
entity: PipeBomb
edges:
- to: cable
completed:
- !type:SpawnPrototype
prototype: CableApcStack1
amount: 5
steps:
- tool: Cutting
doAfter: 2

View File

@@ -190,11 +190,11 @@
state: icon state: icon
- type: construction - type: construction
name: improvised explosive device name: fire bomb
id: improvisedexplosive id: firebomb
graph: ImprovisedExplosive graph: FireBomb
startNode: start startNode: start
targetNode: ied targetNode: firebomb
category: construction-category-weapons category: construction-category-weapons
objectType: Item objectType: Item
description: A weak, improvised incendiary device. description: A weak, improvised incendiary device.
@@ -227,3 +227,16 @@
sprite: Clothing/Head/Hats/straw_hat.rsi sprite: Clothing/Head/Hats/straw_hat.rsi
state: icon state: icon
objectType: Item objectType: Item
- type: construction
name: pipe bomb
id: pipebomb
graph: PipeBomb
startNode: start
targetNode: pipebomb
category: construction-category-weapons
objectType: Item
description: An improvised explosive made from pipes and wire.
icon:
sprite: Objects/Weapons/Bombs/pipebomb.rsi
state: icon

View File

@@ -171,3 +171,18 @@
products: products:
Laughter: 2 Laughter: 2
- type: reaction
id: Gunpowder
impact: Low
quantized: true
minTemp: 374
reactants:
Potassium:
amount: 6
Sulfur:
amount: 2
Charcoal:
amount: 2
effects:
- !type:CreateEntityReactionEffect
entity: MaterialGunpowder

View File

@@ -85,3 +85,11 @@
spawn: MaterialBones1 spawn: MaterialBones1
maxCount: 30 maxCount: 30
itemSize: 1 itemSize: 1
- type: stack
id: Gunpowder
name: gunpowder
icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile }
spawn: MaterialGunpowder
maxCount: 60
itemSize: 1

View File

@@ -117,3 +117,19 @@
lightColor: Orange lightColor: Orange
texturePath: /Textures/Effects/fire.rsi texturePath: /Textures/Effects/fire.rsi
fireStates: 6 fireStates: 6
- type: explosion
id: FireBomb
damagePerIntensity:
types:
Heat: 1
Blunt: 2
Piercing: 3
lightColor: Orange
texturePath: /Textures/Effects/fire.rsi
fireStates: 6
fireStacks: 2
# STOP
# BEFORE YOU ADD MORE EXPLOSION TYPES CONSIDER IF AN EXISTING ONE IS SUITABLE
# ADDING NEW ONES IS PROHIBITIVELY EXPENSIVE

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

View File

@@ -0,0 +1,31 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Pipebomb sprites made by girthquake",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "base"
},
{
"name": "wires"
},
{
"name": "primed",
"delays": [
[
0.2,
0.2,
0.2,
0.2
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

View File

@@ -241,3 +241,8 @@ AirlockShuttleEasyPryLocked: AirlockExternalShuttleLocked
ClothingBackpackFilledDetective: ClothingBackpackSecurityFilledDetective ClothingBackpackFilledDetective: ClothingBackpackSecurityFilledDetective
ClothingBackpackDuffelFilledDetective: ClothingBackpackDuffelSecurityFilledDetective ClothingBackpackDuffelFilledDetective: ClothingBackpackDuffelSecurityFilledDetective
ClothingBackpackSatchelFilledDetective: ClothingBackpackSatchelSecurityFilledDetective ClothingBackpackSatchelFilledDetective: ClothingBackpackSatchelSecurityFilledDetective
# 2024-03-11
ImprovisedExplosive: FireBomb
ImprovisedExplosiveEmpty: FireBombEmpty
ImprovisedExplosiveFuel: FireBombFuel