Decal Spawners (#37066)
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Decals;
|
||||
|
||||
namespace Content.Server.Spawners.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component spawns decals around the entity on MapInit.
|
||||
/// See doc strings for the various parameters for more information.
|
||||
/// </summary>
|
||||
[RegisterComponent, EntityCategory("Spawner")]
|
||||
public sealed partial class RandomDecalSpawnerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of decals to randomly select from when spawning.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<DecalPrototype>> Decals = new();
|
||||
|
||||
/// <summary>
|
||||
/// Radius (in tiles) to spawn decals in. 0 will target only the tile the entity is on.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Radius = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Probability that a particular decal gets spawned.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Prob = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of decals to spawn across the entire radius.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxDecals = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of decals to spawn within a tile.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A value <= 0 or null is considered unlimited.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public int? MaxDecalsPerTile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Whether decals should have a random rotation applied to them.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool RandomRotation = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether decals should snap to 90 degree orientations, does nothing if RandomRotation is false.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool SnapRotation = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether decals should snap to the center omf a grid space or be placed randoly.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A null value will cause this to attempt to use the default value (DefaultSnap) for the decal.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public bool? SnapPosition = false;
|
||||
|
||||
/// <summary>
|
||||
/// zIndex for the generated decals
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int ZIndex = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Color for the generated decals. Does nothing if RandomColorList is set.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Color Color = Color.White;
|
||||
|
||||
/// <summary>
|
||||
/// A random color to select from. Overrides Color if set.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<Color>? RandomColorList = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the new decals are cleanable or not
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A null value will cause this to attempt to use the default value (DefaultCleanable) for the decal.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public bool? Cleanable = null;
|
||||
|
||||
/// <summary>
|
||||
/// A list of tile prototype IDs to only place decals on.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Causes the TileBlacklist to be ignored if this is set.
|
||||
/// Note that due to the nature of tile-based placement, it's possible for decals to "spill over" onto nearby tiles.
|
||||
/// This is mostly so dirt decals don't go on diagonal tiles that won't work for them.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public List<ProtoId<ContentTileDefinition>> TileWhitelist = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of tile prototype IDs to avoid placing decals on.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ignored if TileWhitelist is set.
|
||||
/// Note that due to the nature of tile-based placement, it's possible for decals to "spill over" onto nearby tiles.
|
||||
/// This is mostly so dirt decals don't go on diagonal tiles that won't work for them.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public List<ProtoId<ContentTileDefinition>> TileBlacklist = new();
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether to delete the entity with this component after the spawner is finished.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool DeleteSpawnerAfterSpawn = false;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.Decals;
|
||||
using Content.Server.Spawners.Components;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Spawners.EntitySystems;
|
||||
|
||||
public sealed class RandomDecalSpawnerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DecalSystem _decal = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RandomDecalSpawnerComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, RandomDecalSpawnerComponent component, MapInitEvent args)
|
||||
{
|
||||
TrySpawn(uid);
|
||||
if (component.DeleteSpawnerAfterSpawn)
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
public bool TrySpawn(Entity<RandomDecalSpawnerComponent?> ent)
|
||||
{
|
||||
if (!TryComp<RandomDecalSpawnerComponent>(ent, out var comp))
|
||||
return false;
|
||||
|
||||
if (comp.Decals.Count == 0)
|
||||
return false;
|
||||
|
||||
var tileWhitelist = new List<ITileDefinition>();
|
||||
if (comp.TileWhitelist.Count > 0)
|
||||
{
|
||||
foreach (var tileProto in comp.TileWhitelist)
|
||||
{
|
||||
if (_tileDefs.TryGetDefinition(tileProto, out var tileDef))
|
||||
tileWhitelist.Add(tileDef);
|
||||
}
|
||||
}
|
||||
else if (comp.TileBlacklist.Count > 0)
|
||||
{
|
||||
foreach (var tileDef in _tileDefs)
|
||||
{
|
||||
if (!comp.TileBlacklist.Contains(tileDef.ID))
|
||||
tileWhitelist.Add(tileDef);
|
||||
}
|
||||
}
|
||||
|
||||
var xform = Transform(ent);
|
||||
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
|
||||
return false;
|
||||
|
||||
var addedDecals = new Dictionary<string, int>();
|
||||
|
||||
for (var i = 0; i < comp.MaxDecals; i++)
|
||||
{
|
||||
if (comp.Prob < 1f && _random.NextFloat() > comp.Prob)
|
||||
continue;
|
||||
|
||||
// The vector added here is just to center the generated decals to the tile the spawner is on.
|
||||
var localPos = xform.Coordinates.Position + _random.NextVector2(comp.Radius) + new Vector2(-0.5f, -0.5f);
|
||||
var position = new EntityCoordinates(xform.GridUid.Value, localPos);
|
||||
|
||||
var tileRef = _map.GetTileRef(xform.GridUid.Value, grid, position);
|
||||
|
||||
if (tileWhitelist.Count > 0)
|
||||
{
|
||||
_tileDefs.TryGetDefinition(tileRef.Tile.TypeId, out var currTileDef);
|
||||
if (currTileDef is null || !tileWhitelist.Contains(currTileDef))
|
||||
continue;
|
||||
}
|
||||
|
||||
var tileRefStr = tileRef.ToString();
|
||||
if (comp.MaxDecalsPerTile is > 0)
|
||||
{
|
||||
addedDecals.TryAdd(tileRefStr, 0);
|
||||
if (addedDecals[tileRefStr] >= comp.MaxDecalsPerTile)
|
||||
continue;
|
||||
}
|
||||
|
||||
var decalProtoId = _random.Pick(comp.Decals);
|
||||
var decalProto = _prototypes.Index(decalProtoId);
|
||||
var snapPosition = comp.SnapPosition ?? decalProto.DefaultSnap;
|
||||
if (snapPosition)
|
||||
{
|
||||
position = position.WithPosition(tileRef.GridIndices * grid.TileSize);
|
||||
}
|
||||
|
||||
var cleanable = comp.Cleanable ?? decalProto.DefaultCleanable;
|
||||
|
||||
var rotation = Angle.Zero;
|
||||
if (comp.RandomRotation)
|
||||
{
|
||||
if (comp.SnapRotation)
|
||||
rotation = new Angle((MathF.PI / 2f) * _random.Next(3));
|
||||
else
|
||||
rotation = _random.NextAngle();
|
||||
}
|
||||
|
||||
var color = comp.Color;
|
||||
if (comp.RandomColorList != null && comp.RandomColorList.Count != 0)
|
||||
color = _random.Pick(comp.RandomColorList);
|
||||
|
||||
_decal.TryAddDecal(
|
||||
decalProtoId,
|
||||
position,
|
||||
out _,
|
||||
color,
|
||||
rotation,
|
||||
comp.ZIndex,
|
||||
cleanable
|
||||
);
|
||||
|
||||
if (comp.MaxDecalsPerTile is > 0)
|
||||
addedDecals[tileRefStr]++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user