decal system & crayons (#5183)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
using Content.Shared.Crayon;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Crayon
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class CrayonDecalVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<SpriteComponent>();
|
||||
|
||||
if (component.TryGetData(CrayonVisuals.State, out string state))
|
||||
{
|
||||
sprite.LayerSetState(0, state);
|
||||
}
|
||||
|
||||
if (component.TryGetData(CrayonVisuals.Color, out string color))
|
||||
{
|
||||
sprite.LayerSetColor(0, Color.FromName(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -22,9 +23,8 @@ namespace Content.Client.Crayon.UI
|
||||
|
||||
_menu.OnClose += Close;
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
var crayonDecals = prototypeManager.EnumeratePrototypes<CrayonDecalPrototype>().FirstOrDefault();
|
||||
if (crayonDecals != null)
|
||||
_menu.Populate(crayonDecals);
|
||||
var crayonDecals = prototypeManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||
_menu.Populate(crayonDecals);
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -89,14 +90,12 @@ namespace Content.Client.Crayon.UI
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(CrayonDecalPrototype proto)
|
||||
public void Populate(IEnumerable<DecalPrototype> prototypes)
|
||||
{
|
||||
var path = new ResourcePath(proto.SpritePath);
|
||||
_decals = new Dictionary<string, Texture>();
|
||||
foreach (var state in proto.Decals)
|
||||
foreach (var decalPrototype in prototypes)
|
||||
{
|
||||
var rsi = new SpriteSpecifier.Rsi(path, state);
|
||||
_decals.Add(state, rsi.Frame0());
|
||||
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
|
||||
60
Content.Client/Decals/DecalOverlay.cs
Normal file
60
Content.Client/Decals/DecalOverlay.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Decals
|
||||
{
|
||||
public class DecalOverlay : Overlay
|
||||
{
|
||||
private readonly DecalSystem _system;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
|
||||
|
||||
public DecalOverlay(DecalSystem system, IMapManager mapManager, IPrototypeManager prototypeManager)
|
||||
{
|
||||
_system = system;
|
||||
_mapManager = mapManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var handle = args.WorldHandle;
|
||||
|
||||
Dictionary<string, SpriteSpecifier> cachedTextures = new();
|
||||
|
||||
SpriteSpecifier GetSpriteSpecifier(string id)
|
||||
{
|
||||
if (cachedTextures.TryGetValue(id, out var spriteSpecifier))
|
||||
return spriteSpecifier;
|
||||
|
||||
spriteSpecifier = _prototypeManager.Index<DecalPrototype>(id).Sprite;
|
||||
cachedTextures.Add(id, spriteSpecifier);
|
||||
return spriteSpecifier;
|
||||
}
|
||||
|
||||
foreach (var (gridId, zIndexDictionary) in _system.DecalRenderIndex)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
handle.SetTransform(grid.WorldMatrix);
|
||||
foreach (var (_, decals) in zIndexDictionary)
|
||||
{
|
||||
foreach (var (_, decal) in decals)
|
||||
{
|
||||
var spriteSpecifier = GetSpriteSpecifier(decal.Id);
|
||||
handle.DrawTexture(spriteSpecifier.Frame0(), decal.Coordinates, decal.Angle, decal.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
114
Content.Client/Decals/DecalSystem.cs
Normal file
114
Content.Client/Decals/DecalSystem.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.Decals
|
||||
{
|
||||
public class DecalSystem : SharedDecalSystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
|
||||
private DecalOverlay _overlay = default!;
|
||||
public Dictionary<GridId, SortedDictionary<int, SortedDictionary<uint, Decal>>> DecalRenderIndex = new();
|
||||
private Dictionary<GridId, Dictionary<uint, int>> DecalZIndexIndex = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_overlay = new DecalOverlay(this, MapManager, PrototypeManager);
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
|
||||
SubscribeNetworkEvent<DecalChunkUpdateEvent>(OnChunkUpdate);
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInitialize);
|
||||
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoval);
|
||||
}
|
||||
|
||||
public void ToggleOverlay()
|
||||
{
|
||||
if (_overlayManager.HasOverlay<DecalOverlay>())
|
||||
{
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGridRemoval(GridRemovalEvent ev)
|
||||
{
|
||||
DecalRenderIndex.Remove(ev.GridId);
|
||||
DecalZIndexIndex.Remove(ev.GridId);
|
||||
}
|
||||
|
||||
private void OnGridInitialize(GridInitializeEvent ev)
|
||||
{
|
||||
DecalRenderIndex[ev.GridId] = new();
|
||||
DecalZIndexIndex[ev.GridId] = new();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
protected override bool RemoveDecalHook(GridId gridId, uint uid)
|
||||
{
|
||||
RemoveDecalFromRenderIndex(gridId, uid);
|
||||
|
||||
return base.RemoveDecalHook(gridId, uid);
|
||||
}
|
||||
|
||||
private void RemoveDecalFromRenderIndex(GridId gridId, uint uid)
|
||||
{
|
||||
var zIndex = DecalZIndexIndex[gridId][uid];
|
||||
|
||||
DecalRenderIndex[gridId][zIndex].Remove(uid);
|
||||
if (DecalRenderIndex[gridId][zIndex].Count == 0)
|
||||
DecalRenderIndex[gridId].Remove(zIndex);
|
||||
|
||||
DecalZIndexIndex[gridId].Remove(uid);
|
||||
}
|
||||
|
||||
private void OnChunkUpdate(DecalChunkUpdateEvent ev)
|
||||
{
|
||||
foreach (var (gridId, gridChunks) in ev.Data)
|
||||
{
|
||||
foreach (var (indices, newChunkData) in gridChunks)
|
||||
{
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
if (chunkCollection.TryGetValue(indices, out var chunk))
|
||||
{
|
||||
var removedUids = new HashSet<uint>(chunk.Keys);
|
||||
removedUids.ExceptWith(newChunkData.Keys);
|
||||
foreach (var removedUid in removedUids)
|
||||
{
|
||||
RemoveDecalFromRenderIndex(gridId, removedUid);
|
||||
}
|
||||
}
|
||||
foreach (var (uid, decal) in newChunkData)
|
||||
{
|
||||
if(!DecalRenderIndex[gridId].ContainsKey(decal.ZIndex))
|
||||
DecalRenderIndex[gridId][decal.ZIndex] = new();
|
||||
|
||||
if (DecalZIndexIndex.TryGetValue(gridId, out var values) && values.TryGetValue(uid, out var zIndex))
|
||||
{
|
||||
DecalRenderIndex[gridId][zIndex].Remove(uid);
|
||||
}
|
||||
|
||||
DecalRenderIndex[gridId][decal.ZIndex][uid] = decal;
|
||||
DecalZIndexIndex[gridId][uid] = decal.ZIndex;
|
||||
|
||||
ChunkIndex[gridId][uid] = indices;
|
||||
}
|
||||
chunkCollection[indices] = newChunkData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Content.Client/Decals/ToggleDecalCommand.cs
Normal file
15
Content.Client/Decals/ToggleDecalCommand.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Decals;
|
||||
|
||||
public class ToggleDecalCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "toggledecals";
|
||||
public string Description => "Toggles decaloverlay";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DecalSystem>().ToggleOverlay();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Cleanable;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Server.Decals;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Chemistry.TileReactions
|
||||
@@ -35,6 +37,12 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
}
|
||||
}
|
||||
|
||||
var decalSystem = EntitySystem.Get<DecalSystem>();
|
||||
foreach (var uid in decalSystem.GetDecalsInRange(tile.GridIndex, tile.GridIndices+new Vector2(0.5f, 0.5f), validDelegate: x => x.Cleanable))
|
||||
{
|
||||
decalSystem.RemoveDecal(tile.GridIndex, uid);
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Server.Decals;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
@@ -60,11 +61,8 @@ namespace Content.Server.Crayon
|
||||
Charges = Capacity;
|
||||
|
||||
// Get the first one from the catalog and set it as default
|
||||
var decals = _prototypeManager.EnumeratePrototypes<CrayonDecalPrototype>().FirstOrDefault();
|
||||
if (decals != null)
|
||||
{
|
||||
SelectedState = decals.Decals.First();
|
||||
}
|
||||
var decal = _prototypeManager.EnumeratePrototypes<DecalPrototype>().FirstOrDefault(x => x.Tags.Contains("crayon"));
|
||||
SelectedState = decal?.ID ?? string.Empty;
|
||||
Dirty();
|
||||
}
|
||||
|
||||
@@ -74,14 +72,10 @@ namespace Content.Server.Crayon
|
||||
{
|
||||
case CrayonSelectMessage msg:
|
||||
// Check if the selected state is valid
|
||||
var crayonDecals = _prototypeManager.EnumeratePrototypes<CrayonDecalPrototype>().FirstOrDefault();
|
||||
if (crayonDecals != null)
|
||||
if (_prototypeManager.TryIndex<DecalPrototype>(msg.State, out var prototype) && prototype.Tags.Contains("crayon"))
|
||||
{
|
||||
if (crayonDecals.Decals.Contains(msg.State))
|
||||
{
|
||||
SelectedState = msg.State;
|
||||
Dirty();
|
||||
}
|
||||
SelectedState = msg.State;
|
||||
Dirty();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -131,12 +125,8 @@ namespace Content.Server.Crayon
|
||||
return true;
|
||||
}
|
||||
|
||||
var entity = Owner.EntityManager.SpawnEntity("CrayonDecal", eventArgs.ClickLocation);
|
||||
if (entity.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(CrayonVisuals.State, SelectedState);
|
||||
appearance.SetData(CrayonVisuals.Color, _color);
|
||||
}
|
||||
if(!EntitySystem.Get<DecalSystem>().TryAddDecal(SelectedState, eventArgs.ClickLocation.Offset(new Vector2(-0.5f,-0.5f)), out _, Color.FromName(_color), cleanable: true))
|
||||
return false;
|
||||
|
||||
if (_useSound != null)
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _useSound.GetSound(), Owner, AudioHelpers.WithVariation(0.125f));
|
||||
|
||||
118
Content.Server/Decals/Commands/AddDecalCommand.cs
Normal file
118
Content.Server/Decals/Commands/AddDecalCommand.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Decals.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Mapping)]
|
||||
public sealed class AddDecalCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "adddecal";
|
||||
public string Description => "Creates a decal on the map";
|
||||
public string Help => $"{Command} <id> <x position> <y position> <gridId> [angle=<angle> zIndex=<zIndex> color=<color>]";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 4 || args.Length > 7)
|
||||
{
|
||||
shell.WriteError($"Received invalid amount of arguments arguments. Expected 4 to 7, got {args.Length}.\nUsage: {Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IoCManager.Resolve<IPrototypeManager>().HasIndex<DecalPrototype>(args[0]))
|
||||
{
|
||||
shell.WriteError($"Cannot find decalprototype '{args[0]}'.");
|
||||
}
|
||||
|
||||
if (!float.TryParse(args[1], out var x))
|
||||
{
|
||||
shell.WriteError($"Failed parsing x-coordinate '{args[1]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!float.TryParse(args[2], out var y))
|
||||
{
|
||||
shell.WriteError($"Failed parsing y-coordinate'{args[2]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
if (!int.TryParse(args[3], out var gridIdRaw) || !mapManager.TryGetGrid(new GridId(gridIdRaw), out var grid))
|
||||
{
|
||||
shell.WriteError($"Failed parsing gridId '{args[3]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var coordinates = new EntityCoordinates(grid.GridEntityId, new Vector2(x, y));
|
||||
if (grid.GetTileRef(coordinates).IsSpace())
|
||||
{
|
||||
shell.WriteError($"Cannot create decal on space tile at {coordinates}.");
|
||||
return;
|
||||
}
|
||||
|
||||
Color? color = null;
|
||||
var zIndex = 0;
|
||||
Angle? rotation = null;
|
||||
if (args.Length > 4)
|
||||
{
|
||||
for (int i = 4; i < args.Length; i++)
|
||||
{
|
||||
var rawValue = args[i].Split('=');
|
||||
if (rawValue.Length != 2)
|
||||
{
|
||||
shell.WriteError($"Failed parsing parameter: '{args[i]}'");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (rawValue[0])
|
||||
{
|
||||
case "angle":
|
||||
if (!double.TryParse(rawValue[1], out var degrees))
|
||||
{
|
||||
shell.WriteError($"Failed parsing angle '{rawValue[1]}'.");
|
||||
return;
|
||||
}
|
||||
rotation = Angle.FromDegrees(degrees);
|
||||
break;
|
||||
case "zIndex":
|
||||
if (!int.TryParse(rawValue[1], out zIndex))
|
||||
{
|
||||
shell.WriteError($"Failed parsing zIndex '{rawValue[1]}'.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
if (!Color.TryFromName(rawValue[1], out var colorRaw))
|
||||
{
|
||||
shell.WriteError($"Failed parsing color '{rawValue[1]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
color = colorRaw;
|
||||
break;
|
||||
default:
|
||||
shell.WriteError($"Unknown parameter key '{rawValue[0]}'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(EntitySystem.Get<DecalSystem>().TryAddDecal(args[0], coordinates, out var uid, color, rotation, zIndex))
|
||||
{
|
||||
shell.WriteLine($"Successfully created decal {uid}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteError($"Failed adding decal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Content.Server/Decals/Commands/EditDecalCommand.cs
Normal file
162
Content.Server/Decals/Commands/EditDecalCommand.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Decals;
|
||||
|
||||
[AdminCommand(AdminFlags.Mapping)]
|
||||
public class EditDecalCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "editdecal";
|
||||
public string Description => "Edits a decal.";
|
||||
public string Help => $@"{Command} <gridId> <uid> <mode>\n
|
||||
Possible modes are:\n
|
||||
- position <x position> <y position>\n
|
||||
- color <color>\n
|
||||
- id <id>\n
|
||||
- rotation <degrees>\n
|
||||
- zindex <zIndex>\n
|
||||
- clean <cleanable>
|
||||
";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 4)
|
||||
{
|
||||
shell.WriteError("Expected at least 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var gridIdRaw))
|
||||
{
|
||||
shell.WriteError($"Failed parsing gridId '{args[3]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uint.TryParse(args[1], out var uid))
|
||||
{
|
||||
shell.WriteError($"Failed parsing uid '{args[1]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var gridId = new GridId(gridIdRaw);
|
||||
if (!IoCManager.Resolve<IMapManager>().GridExists(gridId))
|
||||
{
|
||||
shell.WriteError($"No grid with gridId {gridId} exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
var decalSystem = EntitySystem.Get<DecalSystem>();
|
||||
switch (args[2].ToLower())
|
||||
{
|
||||
case "position":
|
||||
if(args.Length != 5)
|
||||
{
|
||||
shell.WriteError("Expected 6 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!float.TryParse(args[3], out var x) || !float.TryParse(args[4], out var y))
|
||||
{
|
||||
shell.WriteError("Failed parsing position.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalPosition(gridId, uid, gridId, new Vector2(x, y)))
|
||||
{
|
||||
shell.WriteError("Failed changing decalposition.");
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
if(args.Length != 4)
|
||||
{
|
||||
shell.WriteError("Expected 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Color.TryFromName(args[3], out var color))
|
||||
{
|
||||
shell.WriteError("Failed parsing color.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalColor(gridId, uid, color))
|
||||
{
|
||||
shell.WriteError("Failed changing decal color.");
|
||||
}
|
||||
break;
|
||||
case "id":
|
||||
if(args.Length != 4)
|
||||
{
|
||||
shell.WriteError("Expected 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalId(gridId, uid, args[3]))
|
||||
{
|
||||
shell.WriteError("Failed changing decal id.");
|
||||
}
|
||||
break;
|
||||
case "rotation":
|
||||
if(args.Length != 4)
|
||||
{
|
||||
shell.WriteError("Expected 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(args[3], out var degrees))
|
||||
{
|
||||
shell.WriteError("Failed parsing degrees.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalRotation(gridId, uid, Angle.FromDegrees(degrees)))
|
||||
{
|
||||
shell.WriteError("Failed changing decal rotation.");
|
||||
}
|
||||
break;
|
||||
case "zindex":
|
||||
if(args.Length != 4)
|
||||
{
|
||||
shell.WriteError("Expected 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[3], out var zIndex))
|
||||
{
|
||||
shell.WriteError("Failed parsing zIndex.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalZIndex(gridId, uid, zIndex))
|
||||
{
|
||||
shell.WriteError("Failed changing decal zIndex.");
|
||||
}
|
||||
break;
|
||||
case "clean":
|
||||
if(args.Length != 4)
|
||||
{
|
||||
shell.WriteError("Expected 5 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bool.TryParse(args[3], out var cleanable))
|
||||
{
|
||||
shell.WriteError("Failed parsing cleanable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decalSystem.SetDecalCleanable(gridId, uid, cleanable))
|
||||
{
|
||||
shell.WriteError("Failed changing decal cleanable flag.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
shell.WriteError("Invalid mode.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Content.Server/Decals/Commands/RemoveDecalCommand.cs
Normal file
46
Content.Server/Decals/Commands/RemoveDecalCommand.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Decals.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Mapping)]
|
||||
public class RemoveDecalCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "rmdecal";
|
||||
public string Description => "removes a decal";
|
||||
public string Help => $"{Command} <uid> <gridId>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteError($"Unexpected number of arguments.\nExpected two: {Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uint.TryParse(args[0], out var uid))
|
||||
{
|
||||
shell.WriteError($"Failed parsing uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[1], out var rawGridId) ||
|
||||
!IoCManager.Resolve<IMapManager>().GridExists(new GridId(rawGridId)))
|
||||
{
|
||||
shell.WriteError("Failed parsing gridId.");
|
||||
}
|
||||
|
||||
var decalSystem = EntitySystem.Get<DecalSystem>();
|
||||
if (decalSystem.RemoveDecal(new GridId(rawGridId), uid))
|
||||
{
|
||||
shell.WriteLine($"Successfully removed decal {uid}.");
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteError($"Failed trying to remove decal {uid}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
326
Content.Server/Decals/DecalSystem.cs
Normal file
326
Content.Server/Decals/DecalSystem.cs
Normal file
@@ -0,0 +1,326 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Decals
|
||||
{
|
||||
public class DecalSystem : SharedDecalSystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly Dictionary<GridId, HashSet<Vector2i>> _dirtyChunks = new();
|
||||
private readonly Dictionary<IPlayerSession, Dictionary<GridId, HashSet<Vector2i>>> _previousSentChunks = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
MapManager.TileChanged += OnTileChanged;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
MapManager.TileChanged -= OnTileChanged;
|
||||
}
|
||||
|
||||
private void OnTileChanged(object? sender, TileChangedEventArgs e)
|
||||
{
|
||||
if (!e.NewTile.IsSpace())
|
||||
return;
|
||||
|
||||
var chunkCollection = ChunkCollection(e.NewTile.GridIndex);
|
||||
var indices = GetChunkIndices(e.NewTile.GridIndices);
|
||||
var toDelete = new HashSet<uint>();
|
||||
if (chunkCollection.TryGetValue(indices, out var chunk))
|
||||
{
|
||||
foreach (var (uid, decal) in chunk)
|
||||
{
|
||||
if (new Vector2((int) Math.Floor(decal.Coordinates.X), (int) Math.Floor(decal.Coordinates.Y)) ==
|
||||
e.NewTile.GridIndices)
|
||||
{
|
||||
toDelete.Add(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete.Count == 0) return;
|
||||
|
||||
foreach (var uid in toDelete)
|
||||
{
|
||||
RemoveDecalInternal(e.NewTile.GridIndex, uid);
|
||||
}
|
||||
|
||||
DirtyChunk(e.NewTile.GridIndex, indices);
|
||||
}
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
switch (e.NewStatus)
|
||||
{
|
||||
case SessionStatus.InGame:
|
||||
_previousSentChunks[e.Session] = new();
|
||||
break;
|
||||
case SessionStatus.Disconnected:
|
||||
_previousSentChunks.Remove(e.Session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DirtyChunk(GridId id, Vector2i chunkIndices)
|
||||
{
|
||||
if(!_dirtyChunks.ContainsKey(id))
|
||||
_dirtyChunks[id] = new HashSet<Vector2i>();
|
||||
_dirtyChunks[id].Add(chunkIndices);
|
||||
}
|
||||
|
||||
public bool TryAddDecal(string id, EntityCoordinates coordinates, [NotNullWhen(true)] out uint? uid, Color? color = null, Angle? rotation = null, int zIndex = 0, bool cleanable = false)
|
||||
{
|
||||
uid = 0;
|
||||
if (!PrototypeManager.HasIndex<DecalPrototype>(id))
|
||||
return false;
|
||||
|
||||
var gridId = coordinates.GetGridId(EntityManager);
|
||||
if (MapManager.GetGrid(gridId).GetTileRef(coordinates).IsSpace())
|
||||
return false;
|
||||
|
||||
rotation ??= Angle.Zero;
|
||||
var decal = new Decal(coordinates.Position, id, color, rotation.Value, zIndex, cleanable);
|
||||
var chunkCollection = DecalGridChunkCollection(gridId);
|
||||
uid = chunkCollection.NextUid++;
|
||||
var chunkIndices = GetChunkIndices(decal.Coordinates);
|
||||
if(!chunkCollection.ChunkCollection.ContainsKey(chunkIndices))
|
||||
chunkCollection.ChunkCollection[chunkIndices] = new();
|
||||
chunkCollection.ChunkCollection[chunkIndices][uid.Value] = decal;
|
||||
ChunkIndex[gridId][uid.Value] = chunkIndices;
|
||||
DirtyChunk(gridId, chunkIndices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveDecal(GridId gridId, uint uid) => RemoveDecalInternal(gridId, uid);
|
||||
|
||||
public HashSet<uint> GetDecalsInRange(GridId gridId, Vector2 position, float distance = 0.75f, Func<Decal, bool>? validDelegate = null)
|
||||
{
|
||||
var uids = new HashSet<uint>();
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var chunkIndices = GetChunkIndices(position);
|
||||
if (!chunkCollection.TryGetValue(chunkIndices, out var chunk))
|
||||
return uids;
|
||||
|
||||
foreach (var (uid, decal) in chunk)
|
||||
{
|
||||
if ((position - decal.Coordinates-new Vector2(0.5f, 0.5f)).Length > distance)
|
||||
continue;
|
||||
|
||||
if (validDelegate == null || validDelegate(decal))
|
||||
{
|
||||
uids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
public bool SetDecalPosition(GridId gridId, uint uid, EntityCoordinates coordinates)
|
||||
{
|
||||
return SetDecalPosition(gridId, uid, coordinates.GetGridId(EntityManager), coordinates.Position);
|
||||
}
|
||||
|
||||
public bool SetDecalPosition(GridId gridId, uint uid, GridId newGridId, Vector2 position)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DirtyChunk(gridId, indices);
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
if (newGridId == gridId)
|
||||
{
|
||||
chunkCollection[indices][uid] = decal.WithCoordinates(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
RemoveDecalInternal(gridId, uid);
|
||||
|
||||
var newChunkCollection = ChunkCollection(newGridId);
|
||||
var chunkIndices = GetChunkIndices(position);
|
||||
if(!newChunkCollection.ContainsKey(chunkIndices))
|
||||
newChunkCollection[chunkIndices] = new();
|
||||
newChunkCollection[chunkIndices][uid] = decal.WithCoordinates(position);
|
||||
ChunkIndex[newGridId][uid] = chunkIndices;
|
||||
DirtyChunk(newGridId, chunkIndices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetDecalColor(GridId gridId, uint uid, Color? color)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
chunkCollection[indices][uid] = decal.WithColor(color);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetDecalId(GridId gridId, uint uid, string id)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PrototypeManager.HasIndex<DecalPrototype>(id))
|
||||
throw new ArgumentOutOfRangeException($"Tried to set decal id to invalid prototypeid: {id}");
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
chunkCollection[indices][uid] = decal.WithId(id);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetDecalRotation(GridId gridId, uint uid, Angle angle)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
chunkCollection[indices][uid] = decal.WithRotation(angle);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetDecalZIndex(GridId gridId, uint uid, int zIndex)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
chunkCollection[indices][uid] = decal.WithZIndex(zIndex);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetDecalCleanable(GridId gridId, uint uid, bool cleanable)
|
||||
{
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
var decal = chunkCollection[indices][uid];
|
||||
chunkCollection[indices][uid] = decal.WithCleanable(cleanable);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
|
||||
foreach (var session in Filter.Broadcast().Recipients)
|
||||
{
|
||||
if(session is not IPlayerSession playerSession || playerSession.Status != SessionStatus.InGame)
|
||||
continue;
|
||||
|
||||
var chunks = GetChunksForSession(playerSession);
|
||||
var updatedChunks = new Dictionary<GridId, HashSet<Vector2i>>();
|
||||
foreach (var (gridId, gridChunks) in chunks)
|
||||
{
|
||||
var newChunks = new HashSet<Vector2i>(gridChunks);
|
||||
if (_previousSentChunks[playerSession].TryGetValue(gridId, out var previousChunks))
|
||||
{
|
||||
newChunks.ExceptWith(previousChunks);
|
||||
}
|
||||
|
||||
if (_dirtyChunks.TryGetValue(gridId, out var dirtyChunks))
|
||||
{
|
||||
gridChunks.IntersectWith(dirtyChunks);
|
||||
newChunks.UnionWith(gridChunks);
|
||||
}
|
||||
|
||||
if (newChunks.Count == 0)
|
||||
continue;
|
||||
|
||||
updatedChunks[gridId] = newChunks;
|
||||
}
|
||||
|
||||
if(updatedChunks.Count == 0)
|
||||
continue;
|
||||
|
||||
_previousSentChunks[playerSession] = chunks;
|
||||
|
||||
//send all gridChunks to client
|
||||
SendChunkUpdates(playerSession, updatedChunks);
|
||||
}
|
||||
|
||||
_dirtyChunks.Clear();
|
||||
}
|
||||
|
||||
private void SendChunkUpdates(IPlayerSession session, Dictionary<GridId, HashSet<Vector2i>> updatedChunks)
|
||||
{
|
||||
var updatedDecals = new Dictionary<GridId, Dictionary<Vector2i, Dictionary<uint, Decal>>>();
|
||||
foreach (var (gridId, chunks) in updatedChunks)
|
||||
{
|
||||
var gridChunks = new Dictionary<Vector2i, Dictionary<uint, Decal>>();
|
||||
foreach (var indices in chunks)
|
||||
{
|
||||
gridChunks.Add(indices,
|
||||
ChunkCollection(gridId).TryGetValue(indices, out var chunk)
|
||||
? chunk
|
||||
: new Dictionary<uint, Decal>());
|
||||
}
|
||||
updatedDecals[gridId] = gridChunks;
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new DecalChunkUpdateEvent{Data = updatedDecals}, Filter.SinglePlayer(session));
|
||||
}
|
||||
|
||||
private HashSet<EntityUid> GetSessionViewers(IPlayerSession session)
|
||||
{
|
||||
var viewers = new HashSet<EntityUid>();
|
||||
if (session.Status != SessionStatus.InGame || session.AttachedEntityUid is null)
|
||||
return viewers;
|
||||
|
||||
viewers.Add(session.AttachedEntityUid.Value);
|
||||
|
||||
foreach (var uid in session.ViewSubscriptions)
|
||||
{
|
||||
viewers.Add(uid);
|
||||
}
|
||||
|
||||
return viewers;
|
||||
}
|
||||
|
||||
private Dictionary<GridId, HashSet<Vector2i>> GetChunksForSession(IPlayerSession session)
|
||||
{
|
||||
return GetChunksForViewers(GetSessionViewers(session));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,16 +72,4 @@ namespace Content.Shared.Crayon
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable, Prototype("crayonDecal")]
|
||||
public class CrayonDecalPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[DataField("id", required: true)]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
[DataField("spritePath")] public string SpritePath { get; } = string.Empty;
|
||||
|
||||
[DataField("decals")] public List<string> Decals { get; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
38
Content.Shared/Decals/Decal.cs
Normal file
38
Content.Shared/Decals/Decal.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public class Decal
|
||||
{
|
||||
[DataField("coordinates")] public readonly Vector2 Coordinates = Vector2.Zero;
|
||||
[DataField("id")] public readonly string Id = string.Empty;
|
||||
[DataField("color")] public readonly Color? Color;
|
||||
[DataField("angle")] public readonly Angle Angle = Angle.Zero;
|
||||
[DataField("zIndex")] public readonly int ZIndex;
|
||||
[DataField("cleanable")] public bool Cleanable;
|
||||
|
||||
public Decal() {}
|
||||
|
||||
public Decal(Vector2 coordinates, string id, Color? color, Angle angle, int zIndex, bool cleanable)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
Id = id;
|
||||
Color = color;
|
||||
Angle = angle;
|
||||
ZIndex = zIndex;
|
||||
Cleanable = cleanable;
|
||||
}
|
||||
|
||||
public Decal WithCoordinates(Vector2 coordinates) => new(coordinates, Id, Color, Angle, ZIndex, Cleanable);
|
||||
public Decal WithId(string id) => new(Coordinates, id, Color, Angle, ZIndex, Cleanable);
|
||||
public Decal WithColor(Color? color) => new(Coordinates, Id, color, Angle, ZIndex, Cleanable);
|
||||
public Decal WithRotation(Angle angle) => new(Coordinates, Id, Color, angle, ZIndex, Cleanable);
|
||||
public Decal WithZIndex(int zIndex) => new(Coordinates, Id, Color, Angle, zIndex, Cleanable);
|
||||
public Decal WithCleanable(bool cleanable) => new(Coordinates, Id, Color, Angle, ZIndex, cleanable);
|
||||
}
|
||||
}
|
||||
15
Content.Shared/Decals/DecalChunkUpdateEvent.cs
Normal file
15
Content.Shared/Decals/DecalChunkUpdateEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class DecalChunkUpdateEvent : EntityEventArgs
|
||||
{
|
||||
public Dictionary<GridId, Dictionary<Vector2i, Dictionary<uint, Decal>>> Data = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class DecalGridChunkCollectionTypeSerializer : ITypeSerializer<DecalGridComponent.DecalGridChunkCollection, MappingDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return serializationManager.ValidateNode<Dictionary<Vector2i, Dictionary<uint, Decal>>>(node, context);
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
//todo this read method does not support pushing inheritance
|
||||
var dictionary =
|
||||
serializationManager.ReadValueOrThrow<Dictionary<Vector2i, Dictionary<uint, Decal>>>(node, context, skipHook);
|
||||
|
||||
var uids = new SortedSet<uint>();
|
||||
var uidChunkMap = new Dictionary<uint, Vector2i>();
|
||||
foreach (var (indices, decals) in dictionary)
|
||||
{
|
||||
foreach (var (uid, _) in decals)
|
||||
{
|
||||
uids.Add(uid);
|
||||
uidChunkMap[uid] = indices;
|
||||
}
|
||||
}
|
||||
|
||||
var uidMap = new Dictionary<uint, uint>();
|
||||
uint nextIndex = 0;
|
||||
foreach (var uid in uids)
|
||||
{
|
||||
uidMap[uid] = nextIndex++;
|
||||
}
|
||||
|
||||
var newDict = new Dictionary<Vector2i, Dictionary<uint, Decal>>();
|
||||
foreach (var (oldUid, newUid) in uidMap)
|
||||
{
|
||||
var indices = uidChunkMap[oldUid];
|
||||
if(!newDict.ContainsKey(indices))
|
||||
newDict[indices] = new();
|
||||
newDict[indices][newUid] = dictionary[indices][oldUid];
|
||||
}
|
||||
|
||||
return new DeserializedValue<DecalGridComponent.DecalGridChunkCollection>(
|
||||
new DecalGridComponent.DecalGridChunkCollection(newDict){NextUid = nextIndex});
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, DecalGridComponent.DecalGridChunkCollection value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return serializationManager.WriteValue(value.ChunkCollection, alwaysWrite, context);
|
||||
}
|
||||
|
||||
public DecalGridComponent.DecalGridChunkCollection Copy(ISerializationManager serializationManager, DecalGridComponent.DecalGridChunkCollection source,
|
||||
DecalGridComponent.DecalGridChunkCollection target, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
var dict = serializationManager.Copy(source.ChunkCollection, target.ChunkCollection, context, skipHook)!;
|
||||
return new DecalGridComponent.DecalGridChunkCollection(dict) {NextUid = source.NextUid};
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Content.Shared/Decals/DecalGridComponent.cs
Normal file
23
Content.Shared/Decals/DecalGridComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
[RegisterComponent]
|
||||
[Friend(typeof(SharedDecalSystem))]
|
||||
public class DecalGridComponent : Component
|
||||
{
|
||||
public override string Name => "DecalGrid";
|
||||
|
||||
[DataField("chunkCollection", serverOnly: true)]
|
||||
public DecalGridChunkCollection ChunkCollection = new(new ());
|
||||
|
||||
public record DecalGridChunkCollection(Dictionary<Vector2i, Dictionary<uint, Decal>> ChunkCollection)
|
||||
{
|
||||
public uint NextUid;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Content.Shared/Decals/DecalPrototype.cs
Normal file
15
Content.Shared/Decals/DecalPrototype.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
[Prototype("decal")]
|
||||
public class DecalPrototype : IPrototype
|
||||
{
|
||||
[DataField("id")] public string ID { get; } = null!;
|
||||
[DataField("sprite")] public SpriteSpecifier Sprite { get; } = SpriteSpecifier.Invalid;
|
||||
[DataField("tags")] public List<string> Tags = new();
|
||||
}
|
||||
}
|
||||
153
Content.Shared/Decals/SharedDecalSystem.cs
Normal file
153
Content.Shared/Decals/SharedDecalSystem.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Decals
|
||||
{
|
||||
public abstract class SharedDecalSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||
|
||||
protected readonly Dictionary<GridId, Dictionary<uint, Vector2i>> ChunkIndex = new();
|
||||
|
||||
public const int ChunkSize = 32;
|
||||
public static Vector2i GetChunkIndices(Vector2 coordinates) => new ((int) Math.Floor(coordinates.X / ChunkSize), (int) Math.Floor(coordinates.Y / ChunkSize));
|
||||
|
||||
private float _viewSize;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInitialize);
|
||||
_configurationManager.OnValueChanged(CVars.NetMaxUpdateRange, OnPvsRangeChanged, true);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_configurationManager.UnsubValueChanged(CVars.NetMaxUpdateRange, OnPvsRangeChanged);
|
||||
}
|
||||
|
||||
private void OnPvsRangeChanged(float obj)
|
||||
{
|
||||
_viewSize = obj * 2f;
|
||||
}
|
||||
|
||||
private void OnGridInitialize(GridInitializeEvent msg)
|
||||
{
|
||||
var comp = EntityManager.EnsureComponent<DecalGridComponent>(MapManager.GetGrid(msg.GridId).GridEntityId);
|
||||
ChunkIndex[msg.GridId] = new();
|
||||
foreach (var (indices, decals) in comp.ChunkCollection.ChunkCollection)
|
||||
{
|
||||
foreach (var uid in decals.Keys)
|
||||
{
|
||||
ChunkIndex[msg.GridId][uid] = indices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected DecalGridComponent.DecalGridChunkCollection DecalGridChunkCollection(GridId gridId) => EntityManager
|
||||
.GetComponent<DecalGridComponent>(MapManager.GetGrid(gridId).GridEntityId).ChunkCollection;
|
||||
protected Dictionary<Vector2i, Dictionary<uint, Decal>> ChunkCollection(GridId gridId) => DecalGridChunkCollection(gridId).ChunkCollection;
|
||||
|
||||
protected virtual void DirtyChunk(GridId id, Vector2i chunkIndices) {}
|
||||
|
||||
protected bool RemoveDecalInternal(GridId gridId, uint uid)
|
||||
{
|
||||
if (!RemoveDecalHook(gridId, uid)) return false;
|
||||
|
||||
if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var chunkCollection = ChunkCollection(gridId);
|
||||
if (!chunkCollection.TryGetValue(indices, out var chunk) || !chunk.Remove(uid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chunkCollection[indices].Count == 0)
|
||||
chunkCollection.Remove(indices);
|
||||
|
||||
ChunkIndex[gridId]?.Remove(uid);
|
||||
DirtyChunk(gridId, indices);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool RemoveDecalHook(GridId gridId, uint uid) => true;
|
||||
|
||||
private (Box2 view, MapId mapId) CalcViewBounds(in EntityUid euid)
|
||||
{
|
||||
var xform = EntityManager.GetComponent<TransformComponent>(euid);
|
||||
|
||||
var view = Box2.UnitCentered.Scale(_viewSize).Translated(xform.WorldPosition);
|
||||
var map = xform.MapID;
|
||||
|
||||
return (view, map);
|
||||
}
|
||||
|
||||
protected Dictionary<GridId, HashSet<Vector2i>> GetChunksForViewers(HashSet<EntityUid> viewers)
|
||||
{
|
||||
var chunks = new Dictionary<GridId, HashSet<Vector2i>>();
|
||||
foreach (var viewerUid in viewers)
|
||||
{
|
||||
var (bounds, mapId) = CalcViewBounds(viewerUid);
|
||||
MapManager.FindGridsIntersectingEnumerator(mapId, bounds, out var gridsEnumerator, true);
|
||||
while(gridsEnumerator.MoveNext(out var grid))
|
||||
{
|
||||
if(!chunks.ContainsKey(grid.Index))
|
||||
chunks[grid.Index] = new();
|
||||
var enumerator = new ChunkIndicesEnumerator(grid.InvWorldMatrix.TransformBox(bounds), ChunkSize);
|
||||
while (enumerator.MoveNext(out var indices))
|
||||
{
|
||||
chunks[grid.Index].Add(indices.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ChunkIndicesEnumerator
|
||||
{
|
||||
private Vector2i _chunkLB;
|
||||
private Vector2i _chunkRT;
|
||||
|
||||
private int _xIndex;
|
||||
private int _yIndex;
|
||||
|
||||
internal ChunkIndicesEnumerator(Box2 localAABB, int chunkSize)
|
||||
{
|
||||
_chunkLB = new Vector2i((int)Math.Floor(localAABB.Left / chunkSize), (int)Math.Floor(localAABB.Bottom / chunkSize));
|
||||
_chunkRT = new Vector2i((int)Math.Floor(localAABB.Right / chunkSize), (int)Math.Floor(localAABB.Top / chunkSize));
|
||||
|
||||
_xIndex = _chunkLB.X;
|
||||
_yIndex = _chunkLB.Y;
|
||||
}
|
||||
|
||||
public bool MoveNext([NotNullWhen(true)] out Vector2i? indices)
|
||||
{
|
||||
if (_yIndex > _chunkRT.Y)
|
||||
{
|
||||
_yIndex = _chunkLB.Y;
|
||||
_xIndex += 1;
|
||||
}
|
||||
|
||||
indices = new Vector2i(_xIndex, _yIndex);
|
||||
_yIndex += 1;
|
||||
|
||||
return _xIndex <= _chunkRT.X;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
- type: crayonDecal
|
||||
id: BaseDecals
|
||||
spritePath: Effects/crayondecals.rsi
|
||||
decals:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
- 8
|
||||
- 9
|
||||
- Blasto
|
||||
- Clandestine
|
||||
- Cyber
|
||||
- Diablo
|
||||
- Donk
|
||||
- Gene
|
||||
- Gib
|
||||
- Max
|
||||
- Newton
|
||||
- North
|
||||
- Omni
|
||||
- Osiron
|
||||
- Prima
|
||||
- Psyke
|
||||
- Sirius
|
||||
- Tunnel
|
||||
- Waffle
|
||||
- a
|
||||
- ampersand
|
||||
- amyjon
|
||||
- antilizard
|
||||
- arrow
|
||||
- b
|
||||
- beepsky
|
||||
- biohazard
|
||||
- blueprint
|
||||
- body
|
||||
- bottle
|
||||
- brush
|
||||
- c
|
||||
- carp
|
||||
- cat
|
||||
- chevron
|
||||
- clawprint
|
||||
- clown
|
||||
- comma
|
||||
- corgi
|
||||
- credit
|
||||
- cyka
|
||||
- d
|
||||
- danger
|
||||
- disk
|
||||
- dot
|
||||
- dwarf
|
||||
- e
|
||||
- electricdanger
|
||||
- end
|
||||
- engie
|
||||
- equals
|
||||
- evac
|
||||
- exclamationmark
|
||||
- f
|
||||
- face
|
||||
- fireaxe
|
||||
- firedanger
|
||||
- food
|
||||
- footprint
|
||||
- g
|
||||
- ghost
|
||||
- guy
|
||||
- h
|
||||
- heart
|
||||
- i
|
||||
- j
|
||||
- k
|
||||
- l
|
||||
- largebrush
|
||||
- like
|
||||
- line
|
||||
- m
|
||||
- matt
|
||||
- med
|
||||
- minus
|
||||
- n
|
||||
- nay
|
||||
- o
|
||||
- p
|
||||
- pawprint
|
||||
- peace
|
||||
- percent
|
||||
- plus
|
||||
- pound
|
||||
- prolizard
|
||||
- q
|
||||
- questionmark
|
||||
- r
|
||||
- radiation
|
||||
- revolution
|
||||
- rune1
|
||||
- rune2
|
||||
- rune3
|
||||
- rune4
|
||||
- rune5
|
||||
- rune6
|
||||
- s
|
||||
- safe
|
||||
- scroll
|
||||
- shop
|
||||
- shortline
|
||||
- shotgun
|
||||
- skull
|
||||
- slash
|
||||
- smallbrush
|
||||
- snake
|
||||
- space
|
||||
- splatter
|
||||
- star
|
||||
- stickman
|
||||
- t
|
||||
- taser
|
||||
- thinline
|
||||
- toilet
|
||||
- toolbox
|
||||
- trade
|
||||
- u
|
||||
- uboa
|
||||
- v
|
||||
- w
|
||||
- x
|
||||
- y
|
||||
- z
|
||||
916
Resources/Prototypes/Decals/crayons.yml
Normal file
916
Resources/Prototypes/Decals/crayons.yml
Normal file
@@ -0,0 +1,916 @@
|
||||
- type: decal
|
||||
id: 0
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 0
|
||||
|
||||
- type: decal
|
||||
id: 1
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 1
|
||||
|
||||
- type: decal
|
||||
id: 2
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 2
|
||||
|
||||
- type: decal
|
||||
id: 3
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 3
|
||||
|
||||
- type: decal
|
||||
id: 4
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 4
|
||||
|
||||
- type: decal
|
||||
id: 5
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 5
|
||||
|
||||
- type: decal
|
||||
id: 6
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 6
|
||||
|
||||
- type: decal
|
||||
id: 7
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 7
|
||||
|
||||
- type: decal
|
||||
id: 8
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 8
|
||||
|
||||
- type: decal
|
||||
id: 9
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: 9
|
||||
|
||||
- type: decal
|
||||
id: Blasto
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Blasto
|
||||
|
||||
- type: decal
|
||||
id: Clandestine
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Clandestine
|
||||
|
||||
- type: decal
|
||||
id: Cyber
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Cyber
|
||||
|
||||
- type: decal
|
||||
id: Diablo
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Diablo
|
||||
|
||||
- type: decal
|
||||
id: Donk
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Donk
|
||||
|
||||
- type: decal
|
||||
id: Gene
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Gene
|
||||
|
||||
- type: decal
|
||||
id: Gib
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Gib
|
||||
|
||||
- type: decal
|
||||
id: Max
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Max
|
||||
|
||||
- type: decal
|
||||
id: Newton
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Newton
|
||||
|
||||
- type: decal
|
||||
id: North
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: North
|
||||
|
||||
- type: decal
|
||||
id: Omni
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Omni
|
||||
|
||||
- type: decal
|
||||
id: Osiron
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Osiron
|
||||
|
||||
- type: decal
|
||||
id: Prima
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Prima
|
||||
|
||||
- type: decal
|
||||
id: Psyke
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Psyke
|
||||
|
||||
- type: decal
|
||||
id: Sirius
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Sirius
|
||||
|
||||
- type: decal
|
||||
id: Tunnel
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Tunnel
|
||||
|
||||
- type: decal
|
||||
id: Waffle
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: Waffle
|
||||
|
||||
- type: decal
|
||||
id: a
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: a
|
||||
|
||||
- type: decal
|
||||
id: ampersand
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: ampersand
|
||||
|
||||
- type: decal
|
||||
id: amyjon
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: amyjon
|
||||
|
||||
- type: decal
|
||||
id: antilizard
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: antilizard
|
||||
|
||||
- type: decal
|
||||
id: arrow
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: arrow
|
||||
|
||||
- type: decal
|
||||
id: b
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: b
|
||||
|
||||
- type: decal
|
||||
id: beepsky
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: beepsky
|
||||
|
||||
- type: decal
|
||||
id: biohazard
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: biohazard
|
||||
|
||||
- type: decal
|
||||
id: blueprint
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: blueprint
|
||||
|
||||
- type: decal
|
||||
id: body
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: body
|
||||
|
||||
- type: decal
|
||||
id: bottle
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: bottle
|
||||
|
||||
- type: decal
|
||||
id: brush
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: brush
|
||||
|
||||
- type: decal
|
||||
id: c
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: c
|
||||
|
||||
- type: decal
|
||||
id: carp
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: carp
|
||||
|
||||
- type: decal
|
||||
id: cat
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: cat
|
||||
|
||||
- type: decal
|
||||
id: chevron
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: chevron
|
||||
|
||||
- type: decal
|
||||
id: clawprint
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: clawprint
|
||||
|
||||
- type: decal
|
||||
id: clown
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: clown
|
||||
|
||||
- type: decal
|
||||
id: comma
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: comma
|
||||
|
||||
- type: decal
|
||||
id: corgi
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: corgi
|
||||
|
||||
- type: decal
|
||||
id: credit
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: credit
|
||||
|
||||
- type: decal
|
||||
id: cyka
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: cyka
|
||||
|
||||
- type: decal
|
||||
id: d
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: d
|
||||
|
||||
- type: decal
|
||||
id: danger
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: danger
|
||||
|
||||
- type: decal
|
||||
id: disk
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: disk
|
||||
|
||||
- type: decal
|
||||
id: dot
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: dot
|
||||
|
||||
- type: decal
|
||||
id: dwarf
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: dwarf
|
||||
|
||||
- type: decal
|
||||
id: e
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: e
|
||||
|
||||
- type: decal
|
||||
id: electricdanger
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: electricdanger
|
||||
|
||||
- type: decal
|
||||
id: end
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: end
|
||||
|
||||
- type: decal
|
||||
id: engie
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: engie
|
||||
|
||||
- type: decal
|
||||
id: equals
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: equals
|
||||
|
||||
- type: decal
|
||||
id: evac
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: evac
|
||||
|
||||
- type: decal
|
||||
id: exclamationmark
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: exclamationmark
|
||||
|
||||
- type: decal
|
||||
id: f
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: f
|
||||
|
||||
- type: decal
|
||||
id: face
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: face
|
||||
|
||||
- type: decal
|
||||
id: fireaxe
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: fireaxe
|
||||
|
||||
- type: decal
|
||||
id: firedanger
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: firedanger
|
||||
|
||||
- type: decal
|
||||
id: food
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: food
|
||||
|
||||
- type: decal
|
||||
id: footprint
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: footprint
|
||||
|
||||
- type: decal
|
||||
id: g
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: g
|
||||
|
||||
- type: decal
|
||||
id: ghost
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: ghost
|
||||
|
||||
- type: decal
|
||||
id: guy
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: guy
|
||||
|
||||
- type: decal
|
||||
id: h
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: h
|
||||
|
||||
- type: decal
|
||||
id: heart
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: heart
|
||||
|
||||
- type: decal
|
||||
id: i
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: i
|
||||
|
||||
- type: decal
|
||||
id: j
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: j
|
||||
|
||||
- type: decal
|
||||
id: k
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: k
|
||||
|
||||
- type: decal
|
||||
id: l
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: l
|
||||
|
||||
- type: decal
|
||||
id: largebrush
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: largebrush
|
||||
|
||||
- type: decal
|
||||
id: like
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: like
|
||||
|
||||
- type: decal
|
||||
id: line
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: line
|
||||
|
||||
- type: decal
|
||||
id: m
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: m
|
||||
|
||||
- type: decal
|
||||
id: matt
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: matt
|
||||
|
||||
- type: decal
|
||||
id: med
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: med
|
||||
|
||||
- type: decal
|
||||
id: minus
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: minus
|
||||
|
||||
- type: decal
|
||||
id: n
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: n
|
||||
|
||||
- type: decal
|
||||
id: nay
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: nay
|
||||
|
||||
- type: decal
|
||||
id: o
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: o
|
||||
|
||||
- type: decal
|
||||
id: p
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: p
|
||||
|
||||
- type: decal
|
||||
id: pawprint
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: pawprint
|
||||
|
||||
- type: decal
|
||||
id: peace
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: peace
|
||||
|
||||
- type: decal
|
||||
id: percent
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: percent
|
||||
|
||||
- type: decal
|
||||
id: plus
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: plus
|
||||
|
||||
- type: decal
|
||||
id: pound
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: pound
|
||||
|
||||
- type: decal
|
||||
id: prolizard
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: prolizard
|
||||
|
||||
- type: decal
|
||||
id: q
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: q
|
||||
|
||||
- type: decal
|
||||
id: questionmark
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: questionmark
|
||||
|
||||
- type: decal
|
||||
id: r
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: r
|
||||
|
||||
- type: decal
|
||||
id: radiation
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: radiation
|
||||
|
||||
- type: decal
|
||||
id: revolution
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: revolution
|
||||
|
||||
- type: decal
|
||||
id: rune1
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune1
|
||||
|
||||
- type: decal
|
||||
id: rune2
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune2
|
||||
|
||||
- type: decal
|
||||
id: rune3
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune3
|
||||
|
||||
- type: decal
|
||||
id: rune4
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune4
|
||||
|
||||
- type: decal
|
||||
id: rune5
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune5
|
||||
|
||||
- type: decal
|
||||
id: rune6
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: rune6
|
||||
|
||||
- type: decal
|
||||
id: s
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: s
|
||||
|
||||
- type: decal
|
||||
id: safe
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: safe
|
||||
|
||||
- type: decal
|
||||
id: scroll
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: scroll
|
||||
|
||||
- type: decal
|
||||
id: shop
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: shop
|
||||
|
||||
- type: decal
|
||||
id: shortline
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: shortline
|
||||
|
||||
- type: decal
|
||||
id: shotgun
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: shotgun
|
||||
|
||||
- type: decal
|
||||
id: skull
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: skull
|
||||
|
||||
- type: decal
|
||||
id: slash
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: slash
|
||||
|
||||
- type: decal
|
||||
id: smallbrush
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: smallbrush
|
||||
|
||||
- type: decal
|
||||
id: snake
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: snake
|
||||
|
||||
- type: decal
|
||||
id: space
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: space
|
||||
|
||||
- type: decal
|
||||
id: splatter
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: splatter
|
||||
|
||||
- type: decal
|
||||
id: star
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: star
|
||||
|
||||
- type: decal
|
||||
id: stickman
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: stickman
|
||||
|
||||
- type: decal
|
||||
id: t
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: t
|
||||
|
||||
- type: decal
|
||||
id: taser
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: taser
|
||||
|
||||
- type: decal
|
||||
id: thinline
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: thinline
|
||||
|
||||
- type: decal
|
||||
id: toilet
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: toilet
|
||||
|
||||
- type: decal
|
||||
id: toolbox
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: toolbox
|
||||
|
||||
- type: decal
|
||||
id: trade
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: trade
|
||||
|
||||
- type: decal
|
||||
id: u
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: u
|
||||
|
||||
- type: decal
|
||||
id: uboa
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: uboa
|
||||
|
||||
- type: decal
|
||||
id: v
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: v
|
||||
|
||||
- type: decal
|
||||
id: w
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: w
|
||||
|
||||
- type: decal
|
||||
id: x
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: x
|
||||
|
||||
- type: decal
|
||||
id: y
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: y
|
||||
|
||||
- type: decal
|
||||
id: z
|
||||
tags: ["crayon"]
|
||||
sprite:
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: z
|
||||
@@ -1,16 +0,0 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
id: CrayonDecal
|
||||
name: crayon drawing
|
||||
description: "Graffiti. Damn kids."
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Physics
|
||||
- type: Sprite
|
||||
sprite: Effects/crayondecals.rsi
|
||||
state: corgi
|
||||
- type: Cleanable
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: CrayonDecalVisualizer
|
||||
Reference in New Issue
Block a user