From 1d2b7131ab6c39222423f336c2ec364dea31ab10 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:55:02 -0700 Subject: [PATCH] Meteors now leave behind a bit of ore (#30419) * Meteors that leave behind asteroid ore * bigger offset * Bit more generic * Better defaults * hrm? * I HATE CUSTOM SERIALIZERS * More comments * renamed a variable --------- Co-authored-by: plykiya --- .../Behaviors/WeightedSpawnEntityBehavior.cs | 89 +++++++++++++++++++ .../EntitySystems/SpawnOnDespawnSystem.cs | 6 ++ .../Entities/Markers/Spawners/temp.yml | 11 +++ .../Weapons/Guns/Projectiles/meteors.yml | 19 +++- .../Prototypes/GameRules/meteorswarms.yml | 14 +++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs create mode 100644 Resources/Prototypes/Entities/Markers/Spawners/temp.yml diff --git a/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs new file mode 100644 index 0000000000..e02ed87322 --- /dev/null +++ b/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs @@ -0,0 +1,89 @@ +using System.Numerics; +using Content.Server.Spawners.Components; +using Content.Server.Spawners.EntitySystems; +using Content.Shared.Random; +using Content.Shared.Random.Helpers; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Spawners; + +namespace Content.Server.Destructible.Thresholds.Behaviors; + +/// +/// Behavior that can be assigned to a trigger that that takes a +/// and spawns a number of the same entity between a given min and max +/// at a random offset from the final position of the entity. +/// +[Serializable] +[DataDefinition] +public sealed partial class WeightedSpawnEntityBehavior : IThresholdBehavior +{ + /// + /// A table of entities with assigned weights to randomly pick from + /// + [DataField(required: true)] + public ProtoId WeightedEntityTable; + + /// + /// How far away to spawn the entity from the parent position + /// + [DataField] + public float SpawnOffset = 1; + + /// + /// The mininum number of entities to spawn randomly + /// + [DataField] + public int MinSpawn = 1; + + /// + /// The max number of entities to spawn randomly + /// + [DataField] + public int MaxSpawn = 1; + + /// + /// Time in seconds to wait before spawning entities + /// + [DataField] + public float SpawnAfter; + + public void Execute(EntityUid uid, DestructibleSystem system, EntityUid? cause = null) + { + // Get the position at which to start initially spawning entities + var transform = system.EntityManager.System(); + var position = transform.GetMapCoordinates(uid); + // Helper function used to randomly get an offset to apply to the original position + Vector2 GetRandomVector() => new (system.Random.NextFloat(-SpawnOffset, SpawnOffset), system.Random.NextFloat(-SpawnOffset, SpawnOffset)); + // Randomly pick the entity to spawn and randomly pick how many to spawn + var entity = system.PrototypeManager.Index(WeightedEntityTable).Pick(system.Random); + var amountToSpawn = system.Random.NextFloat(MinSpawn, MaxSpawn); + + // Different behaviors for delayed spawning and immediate spawning + if (SpawnAfter != 0) + { + // if it fails to get the spawner, this won't ever work so just return + if (!system.PrototypeManager.TryIndex("TemporaryEntityForTimedDespawnSpawners", out var tempSpawnerProto)) + return; + + // spawn the spawner, assign it a lifetime, and assign the entity that it will spawn when despawned + for (var i = 0; i < amountToSpawn; i++) + { + var spawner = system.EntityManager.SpawnEntity(tempSpawnerProto.ID, position.Offset(GetRandomVector())); + system.EntityManager.EnsureComponent(spawner, out var timedDespawnComponent); + timedDespawnComponent.Lifetime = SpawnAfter; + system.EntityManager.EnsureComponent(spawner, out var spawnOnDespawnComponent); + system.EntityManager.System().SetPrototype((spawner, spawnOnDespawnComponent), entity); + } + } + else + { + // directly spawn the desired entities + for (var i = 0; i < amountToSpawn; i++) + { + system.EntityManager.SpawnEntity(entity, position.Offset(GetRandomVector())); + } + } + } +} diff --git a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs index f5a34728dc..2f850faab1 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Spawners.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Spawners; namespace Content.Server.Spawners.EntitySystems; @@ -19,4 +20,9 @@ public sealed class SpawnOnDespawnSystem : EntitySystem Spawn(comp.Prototype, xform.Coordinates); } + + public void SetPrototype(Entity entity, EntProtoId prototype) + { + entity.Comp.Prototype = prototype; + } } diff --git a/Resources/Prototypes/Entities/Markers/Spawners/temp.yml b/Resources/Prototypes/Entities/Markers/Spawners/temp.yml new file mode 100644 index 0000000000..aa76bb5ea7 --- /dev/null +++ b/Resources/Prototypes/Entities/Markers/Spawners/temp.yml @@ -0,0 +1,11 @@ +- type: entity + id: TemporaryEntityForTimedDespawnSpawners + categories: [ HideSpawnMenu, Spawner ] + components: + - type: Transform + anchored: True + - type: Physics + bodyType: Static + canCollide: false + - type: TimedDespawn + # we can't declare the SpawnOnDespawnComponent because the entity is required on yml diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml index 3468cade76..235010acc9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml @@ -112,6 +112,11 @@ sound: collection: MetalBreak - !type:ExplodeBehavior + - !type:WeightedSpawnEntityBehavior + weightedEntityTable: "MeteorSpawnAsteroidWallTable" + minSpawn: 2 + maxSpawn: 4 + spawnAfter: 0.5 - type: entity parent: BaseMeteor @@ -147,6 +152,12 @@ sound: collection: MetalBreak - !type:ExplodeBehavior + - !type:WeightedSpawnEntityBehavior + weightedEntityTable: "MeteorSpawnAsteroidWallTable" + spawnOffset: 2 + minSpawn: 3 + maxSpawn: 6 + spawnAfter: 0.5 - type: entity parent: BaseMeteor @@ -169,6 +180,12 @@ sound: collection: MetalBreak - !type:ExplodeBehavior + - !type:WeightedSpawnEntityBehavior + weightedEntityTable: "MeteorSpawnAsteroidWallTable" + spawnOffset: 3 + minSpawn: 5 + maxSpawn: 8 + spawnAfter: 0.5 - type: entity parent: BaseMeteor @@ -203,4 +220,4 @@ volume: 10 - !type:SpillBehavior solution: blood - - !type:ExplodeBehavior + - !type:ExplodeBehavior \ No newline at end of file diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml index b85032f056..2f1cb4eba5 100644 --- a/Resources/Prototypes/GameRules/meteorswarms.yml +++ b/Resources/Prototypes/GameRules/meteorswarms.yml @@ -17,6 +17,20 @@ GameRuleMeteorSwarmLarge: 5 GameRuleUristSwarm: 0.05 +- type: weightedRandomEntity + id: MeteorSpawnAsteroidWallTable + weights: + AsteroidRock: 10 + AsteroidRockCoal: 5 + AsteroidRockQuartz: 5 + AsteroidRockTin: 5 + AsteroidRockSilver: 2 + AsteroidRockGold: 2 + AsteroidRockPlasma: 2 + AsteroidRockDiamond: 2 + AsteroidRockUranium: 0.5 + AsteroidRockBananium: 0.5 + - type: entity parent: BaseGameRule id: GameRuleMeteorSwarm