From 44f872829382c4c0b5d55145119ff02ca9afd562 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 7 Jul 2024 10:26:58 -0400 Subject: [PATCH] metal foam grenades (#29428) * metal foam grenades * wow okay * meh * bruh * test * push --- .../Effects/AreaReactionEffect.cs | 10 ++-- .../EntitySystems/SmokeOnTriggerSystem.cs | 7 ++- Content.Server/Spreader/SpreaderSystem.cs | 17 ++++++- .../Spreader/EdgeSpreaderPrototype.cs | 6 +++ .../Tiles/ReplaceFloorOnSpawnComponent.cs | 36 +++++++++++++ .../Tiles/ReplaceFloorOnSpawnSystem.cs | 48 ++++++++++++++++++ Resources/Locale/en-US/tiles/tiles.ftl | 3 +- .../Catalog/Cargo/cargo_engineering.yml | 10 ++++ .../Catalog/Fills/Crates/engineering.yml | 11 ++++ .../Entities/Effects/chemistry_effects.yml | 11 +++- .../Objects/Weapons/Throwable/grenades.yml | 15 ++++++ Resources/Prototypes/Tiles/floors.yml | 17 ++++++- Resources/Prototypes/edge_spreaders.yml | 5 ++ .../Grenades/metalfoam.rsi/equipped-BELT.png | Bin 0 -> 240 bytes .../Weapons/Grenades/metalfoam.rsi/icon.png | Bin 0 -> 335 bytes .../Weapons/Grenades/metalfoam.rsi/meta.json | 27 ++++++++++ .../Weapons/Grenades/metalfoam.rsi/primed.png | Bin 0 -> 454 bytes Resources/Textures/Tiles/attributions.yml | 10 ++-- Resources/Textures/Tiles/foammetal.png | Bin 0 -> 2491 bytes 19 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 Content.Shared/Tiles/ReplaceFloorOnSpawnComponent.cs create mode 100644 Content.Shared/Tiles/ReplaceFloorOnSpawnSystem.cs create mode 100644 Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/equipped-BELT.png create mode 100644 Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/primed.png create mode 100644 Resources/Textures/Tiles/foammetal.png diff --git a/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs b/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs index 481f1fc27c5da2..858da2b360569d 100644 --- a/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs +++ b/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs @@ -1,4 +1,5 @@ using Content.Server.Fluids.EntitySystems; +using Content.Server.Spreader; using Content.Shared.Audio; using Content.Shared.Coordinates.Helpers; using Content.Shared.Database; @@ -64,16 +65,19 @@ public override void Effect(EntityEffectBaseArgs args) var transform = reagentArgs.EntityManager.GetComponent(reagentArgs.TargetEntity); var mapManager = IoCManager.Resolve(); var mapSys = reagentArgs.EntityManager.System(); - var sys = reagentArgs.EntityManager.System(); + var spreaderSys = args.EntityManager.System(); + var sys = args.EntityManager.System(); var mapCoords = sys.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform); if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) || - !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef) || - tileRef.Tile.IsSpace()) + !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef)) { return; } + if (spreaderSys.RequiresFloorToSpread(_prototypeId) && tileRef.Tile.IsSpace()) + return; + var coords = mapSys.MapToGrid(gridUid, mapCoords); var ent = reagentArgs.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid()); diff --git a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs index f958373ac74f00..3d3c5d85630c02 100644 --- a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Explosion.Components; using Content.Shared.Explosion.EntitySystems; using Content.Server.Fluids.EntitySystems; +using Content.Server.Spreader; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.Maps; @@ -17,6 +18,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly SmokeSystem _smoke = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly SpreaderSystem _spreader = default!; public override void Initialize() { @@ -31,11 +33,14 @@ private void OnTrigger(EntityUid uid, SmokeOnTriggerComponent comp, TriggerEvent var mapCoords = _transform.GetMapCoordinates(uid, xform); if (!_mapMan.TryFindGridAt(mapCoords, out _, out var grid) || !grid.TryGetTileRef(xform.Coordinates, out var tileRef) || - tileRef.Tile.IsSpace()) + tileRef.Tile.IsEmpty) { return; } + if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && tileRef.Tile.IsSpace()) + return; + var coords = grid.MapToGrid(mapCoords); var ent = Spawn(comp.SmokePrototype, coords.SnapToGrid()); if (!TryComp(ent, out var smoke)) diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 7de8a43d354ff4..50f5d81183b6d3 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Shuttles.Components; using Content.Shared.Atmos; +using Content.Shared.Maps; using Content.Shared.Spreader; using Content.Shared.Tag; using Robust.Shared.Collections; @@ -175,11 +176,12 @@ private void Spread(EntityUid uid, TransformComponent xform, ProtoId public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId prototype, out ValueList<(MapGridComponent, TileRef)> freeTiles, out ValueList occupiedTiles, out ValueList neighbors) { - // TODO remove occupiedTiles -- its currently unused and just slows this method down. - DebugTools.Assert(_prototype.HasIndex(prototype)); freeTiles = []; occupiedTiles = []; neighbors = []; + // TODO remove occupiedTiles -- its currently unused and just slows this method down. + if (!_prototype.TryIndex(prototype, out var spreaderPrototype)) + return; if (!TryComp(comp.GridUid, out var grid)) return; @@ -244,6 +246,9 @@ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId spreader) + { + if (!_prototype.Index(spreader).TryGetComponent(out var spreaderComp, EntityManager.ComponentFactory)) + return false; + + return _prototype.Index(spreaderComp.Id).PreventSpreadOnSpaced; + } } diff --git a/Content.Shared/Spreader/EdgeSpreaderPrototype.cs b/Content.Shared/Spreader/EdgeSpreaderPrototype.cs index fee8f93a6d3c46..33665d82b5dff6 100644 --- a/Content.Shared/Spreader/EdgeSpreaderPrototype.cs +++ b/Content.Shared/Spreader/EdgeSpreaderPrototype.cs @@ -10,4 +10,10 @@ public sealed partial class EdgeSpreaderPrototype : IPrototype { [IdDataField] public string ID { get; } = string.Empty; [DataField(required:true)] public int UpdatesPerSecond; + + /// + /// If true, this spreader can't spread onto spaced tiles like lattice. + /// + [DataField] + public bool PreventSpreadOnSpaced = true; } diff --git a/Content.Shared/Tiles/ReplaceFloorOnSpawnComponent.cs b/Content.Shared/Tiles/ReplaceFloorOnSpawnComponent.cs new file mode 100644 index 00000000000000..1b87082defad73 --- /dev/null +++ b/Content.Shared/Tiles/ReplaceFloorOnSpawnComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.Maps; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Tiles; + +/// +/// Replaces floor tiles around this entity when it spawns +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ReplaceFloorOnSpawnSystem))] +public sealed partial class ReplaceFloorOnSpawnComponent : Component +{ + /// + /// The floor tiles that will be replaced. If null, will replace all. + /// + [DataField] + public List>? ReplaceableTiles = new(); + + /// + /// The tiles that it will replace. Randomly picked from the list. + /// + [DataField] + public List> ReplacementTiles = new(); + + /// + /// Whether or not there has to be a tile in the location to be replaced. + /// + [DataField] + public bool ReplaceSpace = true; + + /// + /// List of offsets from the base tile, used to determine which tiles will be replaced. + /// + [DataField] + public List Offsets = new() { Vector2i.Up, Vector2i.Down, Vector2i.Left, Vector2i.Right, Vector2i.Zero }; +} diff --git a/Content.Shared/Tiles/ReplaceFloorOnSpawnSystem.cs b/Content.Shared/Tiles/ReplaceFloorOnSpawnSystem.cs new file mode 100644 index 00000000000000..818991f823dd79 --- /dev/null +++ b/Content.Shared/Tiles/ReplaceFloorOnSpawnSystem.cs @@ -0,0 +1,48 @@ +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Shared.Tiles; + +public sealed class ReplaceFloorOnSpawnSystem : EntitySystem +{ + [Dependency] private readonly ITileDefinitionManager _tile = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var xform = Transform(ent); + if (xform.GridUid is not { } grid || !TryComp(grid, out var gridComp)) + return; + + if (ent.Comp.ReplaceableTiles != null && ent.Comp.ReplaceableTiles.Count == 0) + return; + + var tileIndices = _map.LocalToTile(grid, gridComp, xform.Coordinates); + + foreach (var offset in ent.Comp.Offsets) + { + var actualIndices = tileIndices + offset; + + if (!_map.TryGetTileRef(grid, gridComp, actualIndices, out var tile)) + continue; + + if (ent.Comp.ReplaceableTiles != null && + !tile.Tile.IsEmpty && + !ent.Comp.ReplaceableTiles.Contains(_tile[tile.Tile.TypeId].ID)) + continue; + + var tileToSet = _random.Pick(ent.Comp.ReplacementTiles); + _map.SetTile(grid, gridComp, tile.GridIndices, new Tile(_prototype.Index(tileToSet).TileId)); + } + } +} diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl index e5b6810fcab7e0..35cea19f7862e6 100644 --- a/Resources/Locale/en-US/tiles/tiles.ftl +++ b/Resources/Locale/en-US/tiles/tiles.ftl @@ -87,6 +87,7 @@ tiles-gold-tile = gold tile tiles-silver-tile = silver tile tiles-glass-floor = glass floor tiles-reinforced-glass-floor = reinforced glass floor +tiles-metal-foam = metal foam floor tiles-green-circuit-floor = green circuit floor tiles-blue-circuit-floor = blue circuit floor tiles-snow = snow @@ -126,4 +127,4 @@ tiles-mowed-astro-grass = mowed astro-grass tiles-jungle-astro-grass = jungle astro-grass tiles-astro-ice = astro-ice tiles-astro-snow = astro-snow -tiles-wood-large = large wood \ No newline at end of file +tiles-wood-large = large wood diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml index 7ca6af84518d53..d4e2b4c60dc8a6 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_engineering.yml @@ -28,6 +28,16 @@ category: cargoproduct-category-name-engineering group: market +- type: cargoProduct + id: EngineeringFoamGrenade + icon: + sprite: Objects/Weapons/Grenades/metalfoam.rsi + state: icon + product: CrateEngineeringFoamGrenade + cost: 2500 + category: cargoproduct-category-name-engineering + group: market + - type: cargoProduct id: EngineeringCableBulk icon: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml index 26a8910c735e95..62d07b0beda278 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml @@ -76,6 +76,17 @@ - id: CableHVStack amount: 3 +- type: entity + id: CrateEngineeringFoamGrenade + parent: CrateEngineeringSecure + name: sealant grenade crate + description: 5 metal foam sealant grenades. + components: + - type: StorageFill + contents: + - id: MetalFoamGrenade + amount: 5 + - type: entity id: CrateEngineeringCableBulk parent: CrateElectrical diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index 096e88bcb6fef0..ee300e9aeafff5 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -101,6 +101,8 @@ state: m_foam-north - map: [ "enum.EdgeLayer.West" ] state: m_foam-west + - type: EdgeSpreader + id: MetalFoam - type: FoamVisuals animationTime: 0.6 animationState: m_foam-dissolve @@ -135,7 +137,7 @@ - type: RCDDeconstructable cost: 2 delay: 2 - fx: EffectRCDDeconstruct2 + fx: EffectRCDDeconstruct2 - type: Clickable - type: InteractionOutline - type: Sprite @@ -159,6 +161,13 @@ - type: Transform anchored: true - type: Airtight + - type: ReplaceFloorOnSpawn + replaceableTiles: + - Plating + - Lattice + - TrainLattice + replacementTiles: + - FloorMetalFoam - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index b1d260c32761f1..eb382c01e5fc57 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -422,6 +422,21 @@ - ReagentId: TearGas Quantity: 50 +- type: entity + parent: SmokeGrenade + id: MetalFoamGrenade + name: metal foam grenade + description: An emergency tool used for patching up holes. Almost as good as real walls. + components: + - type: Sprite + sprite: Objects/Weapons/Grenades/metalfoam.rsi + - type: SmokeOnTrigger + duration: 10 + spreadAmount: 13 + smokePrototype: AluminiumMetalFoam + - type: StaticPrice + price: 350 + # Non-explosive "dummy" grenades to use as a distraction. - type: entity diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 602e9bc441cc8e..91b61bec0942c1 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -201,7 +201,7 @@ collection: FootstepHull itemDrop: FloorTileItemBrassFilled heatCapacity: 10000 - + - type: tile id: FloorBrassReebe name: tiles-brass-floor-reebe @@ -1391,6 +1391,21 @@ itemDrop: SheetRGlass1 heatCapacity: 10000 +- type: tile + id: FloorMetalFoam + name: tiles-metal-foam + sprite: /Textures/Tiles/foammetal.png + variants: 1 + placementVariants: + - 1.0 + baseTurf: Plating + isSubfloor: false + deconstructTools: [ Prying ] + footstepSounds: + collection: FootstepHull + itemDrop: SheetSteel1 + heatCapacity: 10000 + # Circuits - type: tile id: FloorGreenCircuit diff --git a/Resources/Prototypes/edge_spreaders.yml b/Resources/Prototypes/edge_spreaders.yml index 061932c706db3c..c93cc02ba9774e 100644 --- a/Resources/Prototypes/edge_spreaders.yml +++ b/Resources/Prototypes/edge_spreaders.yml @@ -9,3 +9,8 @@ - type: edgeSpreader id: Smoke updatesPerSecond: 8 + +- type: edgeSpreader + id: MetalFoam + updatesPerSecond: 16 + preventSpreadOnSpaced: false diff --git a/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/equipped-BELT.png b/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/equipped-BELT.png new file mode 100644 index 0000000000000000000000000000000000000000..d3cf1cf4c9329b5e0ba25716b5eee1c78ee13068 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%KdAc};RLpsM z%aH3(fB?${*PL9|)pHIVvvbvQoFb#ScKSyxGodw2Q+s=R7qp*vmi%BZ^O*x%b_)Wv zGALgu{W~}MxRUmY*SqpVLob?~O;Pr_IMM7t`rgjB4@Gt-&lZhbk~~v%OZ?o&WwpmY z|2x&Ca|tNDG=K5?-x6(#N^+4Kk6MFFVqj?aKl!8fgfcmO-Php>+n7`SaxIyA-23z| i(HUR1^V5~j%8-*tbW(7bvhNV%u0pUXO@geCxb0bm#a literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/icon.png b/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a13dedfc2052c67c1346cc71dc821facda8e1cd4 GIT binary patch literal 335 zcmV-V0kHmwP)Px$2}wjjR9J=WmN9F>KorM+X)7^fE3`|<;v}7fF6~#4toAeHqt&T2^b>T~>A+Ir z5Rz&IQCuV?Q735$UG7C1g!@gm{O|qmy?gf#3`o&=USWc^Rn*?F^Jo4}eli5*vUsvGPc0XCMqik|e3mK==NEUgwsZ%PXd< zCF9YMx0h$J0qqj3vTwGQMd38b7IW-87Y7#^u=AX3F~=zi?AA43v(HKfwA;{i-9PWZ zzqO(7`$ugs*XZ%51b4s$Kwg5{XMwh;JPnmn hl}2*y{{e%+um|$mboU?%v_Jp=002ovPDHLkV1h5Fli2_O literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/meta.json b/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/meta.json new file mode 100644 index 00000000000000..139eebb04da79d --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by EmoGarbage404", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "primed", + "delays": [ + [ + 0.2, + 0.1 + ] + ] + }, + { + "name": "equipped-BELT", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/primed.png b/Resources/Textures/Objects/Weapons/Grenades/metalfoam.rsi/primed.png new file mode 100644 index 0000000000000000000000000000000000000000..dafc378c3bca93b686e1ae223ae5c700132750d1 GIT binary patch literal 454 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