mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Refactor tile IDs. (#778)
* Refactor tile IDs. Tile IDs are now automatically assigned at runtime. A mapping is also stored in map files to avoid compatibility issues. Also removed tile prototypes. Content is now responsible for it. This is so content can define its own data for tiles. * Update map format specification. * Fix tile placement.
This commit is contained in:
committed by
GitHub
parent
2bb73aa93d
commit
d7d0363cc3
@@ -71,7 +71,6 @@ namespace SS14.Client
|
||||
[Dependency] private readonly IResourceManager _resourceManager;
|
||||
[Dependency] private readonly ISS14Serializer _serializer;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IClientNetManager _networkManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IStateManager _stateManager;
|
||||
@@ -160,7 +159,6 @@ namespace SS14.Client
|
||||
_console.Initialize();
|
||||
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
|
||||
_prototypeManager.Resync();
|
||||
_tileDefinitionManager.Initialize();
|
||||
_mapManager.Initialize();
|
||||
_lightManager.Initialize();
|
||||
_entityManager.Initialize();
|
||||
|
||||
@@ -25,21 +25,22 @@ namespace SS14.Client.Map
|
||||
TileSet = new Godot.TileSet();
|
||||
}
|
||||
|
||||
public override ushort Register(ITileDefinition tileDef)
|
||||
public override void Register(ITileDefinition tileDef)
|
||||
{
|
||||
var ret = base.Register(tileDef);
|
||||
base.Register(tileDef);
|
||||
|
||||
TileSet.CreateTile(ret);
|
||||
if (!string.IsNullOrEmpty(tileDef.SpriteName))
|
||||
var id = tileDef.TileId;
|
||||
TileSet.CreateTile(id);
|
||||
if (string.IsNullOrEmpty(tileDef.SpriteName))
|
||||
{
|
||||
var texture =
|
||||
resourceCache.GetResource<TextureResource>(
|
||||
new ResourcePath("/Textures/Tiles/") / $@"{tileDef.SpriteName}.png");
|
||||
TileSet.TileSetTexture(ret, texture.Texture.GodotTexture);
|
||||
Textures[ret] = texture;
|
||||
return;
|
||||
}
|
||||
|
||||
return ret;
|
||||
var texture =
|
||||
resourceCache.GetResource<TextureResource>(
|
||||
new ResourcePath("/Textures/Tiles/") / $@"{tileDef.SpriteName}.png");
|
||||
TileSet.TileSetTexture(id, texture.Texture.GodotTexture);
|
||||
Textures[id] = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Reflection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SS14.Shared.Utility;
|
||||
|
||||
@@ -71,11 +72,11 @@ namespace SS14.Client.UserInterface.CustomControls
|
||||
{
|
||||
TileList.DisposeAllChildren();
|
||||
|
||||
var tileDefs = tileDefinitionManager.Select(td => td.Name);
|
||||
IEnumerable<ITileDefinition> tileDefs = tileDefinitionManager;
|
||||
|
||||
if (!string.IsNullOrEmpty(searchStr))
|
||||
{
|
||||
tileDefs = tileDefs.Where(s => s.IndexOf(searchStr, StringComparison.InvariantCultureIgnoreCase) >= 0);
|
||||
tileDefs = tileDefs.Where(s => s.DisplayName.IndexOf(searchStr, StringComparison.InvariantCultureIgnoreCase) >= 0);
|
||||
}
|
||||
|
||||
foreach (var entry in tileDefs)
|
||||
@@ -84,7 +85,7 @@ namespace SS14.Client.UserInterface.CustomControls
|
||||
{
|
||||
TileDef = entry,
|
||||
};
|
||||
button.ActualButton.Text = entry;
|
||||
button.ActualButton.Text = entry.DisplayName;
|
||||
button.ActualButton.OnToggled += OnItemButtonToggled;
|
||||
|
||||
TileList.AddChild(button);
|
||||
@@ -93,7 +94,7 @@ namespace SS14.Client.UserInterface.CustomControls
|
||||
|
||||
private class TileSpawnButton : PanelContainer
|
||||
{
|
||||
public string TileDef { get; set; }
|
||||
public ITileDefinition TileDef { get; set; }
|
||||
public Button ActualButton { get; private set; }
|
||||
|
||||
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Placement/TileSpawnItem.tscn");
|
||||
@@ -134,7 +135,7 @@ namespace SS14.Client.UserInterface.CustomControls
|
||||
var newObjInfo = new PlacementInformation
|
||||
{
|
||||
PlacementOption = "AlignTileAny",
|
||||
TileType = tileDefinitionManager[item.TileDef].TileId,
|
||||
TileType = item.TileDef.TileId,
|
||||
Range = 400,
|
||||
IsTile = true
|
||||
};
|
||||
|
||||
@@ -225,7 +225,6 @@ namespace SS14.Server
|
||||
prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes"));
|
||||
prototypeManager.Resync();
|
||||
|
||||
IoCManager.Resolve<ITileDefinitionManager>().Initialize();
|
||||
IoCManager.Resolve<IConsoleShell>().Initialize();
|
||||
IoCManager.Resolve<IConGroupController>().Initialize();
|
||||
|
||||
|
||||
@@ -179,6 +179,11 @@ namespace SS14.Server.Maps
|
||||
/// </summary>
|
||||
private class MapContext : YamlObjectSerializer.Context, IEntityFinishContext
|
||||
{
|
||||
[Dependency]
|
||||
private readonly IMapManager _mapManager;
|
||||
[Dependency]
|
||||
private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
|
||||
public readonly Dictionary<GridId, int> GridIDMap = new Dictionary<GridId, int>();
|
||||
public readonly List<IMapGrid> Grids = new List<IMapGrid>();
|
||||
|
||||
@@ -194,13 +199,17 @@ namespace SS14.Server.Maps
|
||||
string CurrentWritingComponent;
|
||||
IEntity CurrentWritingEntity;
|
||||
|
||||
private Dictionary<ushort, string> _tileMap;
|
||||
|
||||
public MapContext()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RootNode = new YamlMappingNode();
|
||||
}
|
||||
|
||||
public MapContext(YamlMappingNode node, IMap targetMap)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RootNode = node;
|
||||
TargetMap = targetMap;
|
||||
}
|
||||
@@ -209,6 +218,7 @@ namespace SS14.Server.Maps
|
||||
public void Deserialize()
|
||||
{
|
||||
ReadMetaSection();
|
||||
ReadTileMapSection();
|
||||
ReadGridSection();
|
||||
|
||||
// Entities are allocated in a separate step so entity UID cross references can be resolved.
|
||||
@@ -226,21 +236,35 @@ namespace SS14.Server.Maps
|
||||
}
|
||||
}
|
||||
|
||||
void ReadTileMapSection()
|
||||
{
|
||||
_tileMap = new Dictionary<ushort, string>();
|
||||
|
||||
var tileMap = RootNode.GetNode<YamlMappingNode>("tilemap");
|
||||
foreach (var (key, value) in tileMap)
|
||||
{
|
||||
var tileId = (ushort) key.AsInt();
|
||||
var tileDefName = value.AsString();
|
||||
_tileMap.Add(tileId, tileDefName);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadGridSection()
|
||||
{
|
||||
var grids = RootNode.GetNode<YamlSequenceNode>("grids");
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
foreach (var grid in grids)
|
||||
{
|
||||
var newId = new GridId?();
|
||||
YamlGridSerializer.DeserializeGrid(
|
||||
mapMan, TargetMap, ref newId,
|
||||
_mapManager, TargetMap, ref newId,
|
||||
(YamlMappingNode)grid["settings"],
|
||||
(YamlSequenceNode)grid["chunks"]
|
||||
(YamlSequenceNode)grid["chunks"],
|
||||
_tileMap,
|
||||
_tileDefinitionManager
|
||||
);
|
||||
|
||||
Grids.Add(mapMan.GetGrid(newId.Value));
|
||||
Grids.Add(_mapManager.GetGrid(newId.Value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +320,7 @@ namespace SS14.Server.Maps
|
||||
public YamlNode Serialize()
|
||||
{
|
||||
WriteMetaSection();
|
||||
WriteTileMapSection();
|
||||
WriteGridSection();
|
||||
|
||||
PopulateEntityList();
|
||||
@@ -314,6 +339,16 @@ namespace SS14.Server.Maps
|
||||
meta.Add("author", "Space-Wizards");
|
||||
}
|
||||
|
||||
void WriteTileMapSection()
|
||||
{
|
||||
var tileMap = new YamlMappingNode();
|
||||
RootNode.Add("tilemap", tileMap);
|
||||
foreach (var tileDefinition in _tileDefinitionManager)
|
||||
{
|
||||
tileMap.Add(tileDefinition.TileId.ToString(CultureInfo.InvariantCulture), tileDefinition.Name);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteGridSection()
|
||||
{
|
||||
var grids = new YamlSequenceNode();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -77,7 +78,7 @@ namespace SS14.Server.Maps
|
||||
return Convert.ToBase64String(barr);
|
||||
}
|
||||
|
||||
public static void DeserializeGrid(IMapManager mapMan, IMap map, ref GridId? gridId, YamlMappingNode info, YamlSequenceNode chunks)
|
||||
public static void DeserializeGrid(IMapManager mapMan, IMap map, ref GridId? gridId, YamlMappingNode info, YamlSequenceNode chunks, IReadOnlyDictionary<ushort, string> tileDefMapping, ITileDefinitionManager tileDefinitionManager)
|
||||
{
|
||||
ushort csz = 0;
|
||||
ushort tsz = 0;
|
||||
@@ -104,11 +105,11 @@ namespace SS14.Server.Maps
|
||||
|
||||
foreach (YamlMappingNode chunkNode in chunks.Cast<YamlMappingNode>())
|
||||
{
|
||||
DeserializeChunk(mapMan, grid, chunkNode);
|
||||
DeserializeChunk(mapMan, grid, chunkNode, tileDefMapping, tileDefinitionManager);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeserializeChunk(IMapManager mapMan, IMapGrid grid, YamlMappingNode chunk)
|
||||
private static void DeserializeChunk(IMapManager mapMan, IMapGrid grid, YamlMappingNode chunk, IReadOnlyDictionary<ushort, string> tileDefMapping, ITileDefinitionManager tileDefinitionManager)
|
||||
{
|
||||
var indNode = chunk["ind"];
|
||||
var tileNode = chunk["tiles"];
|
||||
@@ -128,6 +129,9 @@ namespace SS14.Server.Maps
|
||||
var id = reader.ReadUInt16();
|
||||
var data = reader.ReadUInt16();
|
||||
|
||||
var defName = tileDefMapping[id];
|
||||
id = tileDefinitionManager[defName].TileId;
|
||||
|
||||
var tile = new Tile(id, data);
|
||||
grid.SetTile(new GridCoordinates(x + indices.X, y + indices.Y, grid), tile);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using SS14.Shared.Map;
|
||||
|
||||
namespace SS14.Shared.Interfaces.Map
|
||||
namespace SS14.Shared.Interfaces.Map
|
||||
{
|
||||
/// <summary>
|
||||
/// The definition (template) for a grid tile.
|
||||
@@ -8,54 +6,29 @@ namespace SS14.Shared.Interfaces.Map
|
||||
public interface ITileDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The internal ID of the tile definition.
|
||||
/// The numeric tile ID used to refer to this tile inside the map datastructure.
|
||||
/// </summary>
|
||||
ushort TileId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the definition.
|
||||
/// The name of the definition. This is user facing.
|
||||
/// </summary>
|
||||
string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal name of the definition.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
bool IsConnectingSprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the tile nontransparent?
|
||||
/// </summary>
|
||||
bool IsOpaque { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Are items stopped by this tile?
|
||||
/// </summary>
|
||||
bool IsCollidable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Can this tile contain a gas?
|
||||
/// </summary>
|
||||
bool IsGasVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Does this tile vent gas into space?
|
||||
/// </summary>
|
||||
bool IsVentedIntoSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this tile a floor?
|
||||
/// </summary>
|
||||
bool IsFloor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the sprite to draw.
|
||||
/// </summary>
|
||||
string SpriteName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Assign a new value to <see cref="TileId"/>, used when registering the tile definition.
|
||||
/// </summary>
|
||||
/// <param name="data">Optional data to add to this tile.</param>
|
||||
/// <returns></returns>
|
||||
Tile Create(ushort data = 0);
|
||||
/// <param name="id">The new tile ID for this tile definition.</param>
|
||||
void AssignTileId(ushort id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace SS14.Shared.Interfaces.Map
|
||||
/// Register a definition with this manager.
|
||||
/// </summary>
|
||||
/// <param name="tileDef">THe definition to register.</param>
|
||||
/// <returns>The internal id of the registered definition.</returns>
|
||||
ushort Register(ITileDefinition tileDef);
|
||||
void Register(ITileDefinition tileDef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Prototypes;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace SS14.Shared.Map
|
||||
{
|
||||
// Instantiated by the Prototype system through reflection.
|
||||
[Prototype("tile")]
|
||||
public sealed class PrototypeTileDefinition : TileDefinition, IPrototype
|
||||
{
|
||||
internal ushort FutureID { get; private set; }
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
Name = mapping.GetNode("name").ToString();
|
||||
SpriteName = mapping.GetNode("texture").ToString();
|
||||
FutureID = (ushort)mapping.GetNode("id").AsInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.Utility;
|
||||
|
||||
namespace SS14.Shared.Map
|
||||
{
|
||||
[DebuggerDisplay("TileDef: {Name}")]
|
||||
public abstract class TileDefinition : ITileDefinition
|
||||
{
|
||||
private ushort _tileId = ushort.MaxValue;
|
||||
|
||||
public ushort TileId
|
||||
{
|
||||
get
|
||||
{
|
||||
DebugTools.Assert(_tileId != ushort.MaxValue);
|
||||
return _tileId;
|
||||
}
|
||||
}
|
||||
|
||||
public void Register(ITileDefinitionManager tileDefinitionManager)
|
||||
{
|
||||
_tileId = tileDefinitionManager.Register(this);
|
||||
}
|
||||
|
||||
public string Name { get; protected set; }
|
||||
|
||||
public bool IsConnectingSprite { get; protected set; }
|
||||
|
||||
public bool IsOpaque { get; protected set; }
|
||||
|
||||
public bool IsCollidable { get; protected set; }
|
||||
|
||||
public bool IsGasVolume { get; protected set; }
|
||||
|
||||
public bool IsVentedIntoSpace { get; protected set; }
|
||||
|
||||
public bool IsFloor { get; protected set; }
|
||||
|
||||
public string SpriteName { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tile instance from this definition.
|
||||
/// </summary>
|
||||
/// <param name="data">Optional per-tile data.</param>
|
||||
/// <returns></returns>
|
||||
public Tile Create(ushort data = 0)
|
||||
{
|
||||
return new Tile(TileId, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,30 +28,21 @@ namespace SS14.Shared.Map
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
foreach (var prototype in PrototypeManager.EnumeratePrototypes<PrototypeTileDefinition>().OrderBy(p => p.FutureID))
|
||||
{
|
||||
prototype.Register(this);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ushort Register(ITileDefinition tileDef)
|
||||
public virtual void Register(ITileDefinition tileDef)
|
||||
{
|
||||
if (_tileIds.TryGetValue(tileDef, out ushort id))
|
||||
{
|
||||
throw new InvalidOperationException($"TileDefinition is already registered: {tileDef.GetType()}, id: {id}");
|
||||
}
|
||||
|
||||
var name = tileDef.Name;
|
||||
if (_tileNames.ContainsKey(name))
|
||||
{
|
||||
throw new ArgumentException("Another tile definition with the same name has already been registered.", nameof(tileDef));
|
||||
}
|
||||
|
||||
id = checked((ushort) TileDefs.Count);
|
||||
var id = checked((ushort) TileDefs.Count);
|
||||
tileDef.AssignTileId(id);
|
||||
TileDefs.Add(tileDef);
|
||||
_tileNames[name] = tileDef;
|
||||
_tileIds[tileDef] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
public ITileDefinition this[string name] => _tileNames[name];
|
||||
|
||||
@@ -192,11 +192,9 @@
|
||||
<Compile Include="Map\GridId.cs" />
|
||||
<Compile Include="Map\Coordinates.cs" />
|
||||
<Compile Include="Map\MapId.cs" />
|
||||
<Compile Include="Map\DefaultTileDefinitions.cs" />
|
||||
<Compile Include="Map\MapManager.cs" />
|
||||
<Compile Include="Map\MapManager.Network.cs" />
|
||||
<Compile Include="Map\Tile.cs" />
|
||||
<Compile Include="Map\TileDefinition.cs" />
|
||||
<Compile Include="Map\TileDefinitionManager.cs" />
|
||||
<Compile Include="Map\TileRef.cs" />
|
||||
<Compile Include="Network\Messages\MsgState.cs" />
|
||||
|
||||
@@ -20,6 +20,12 @@ Fields:
|
||||
* `name`: A name. Simple huh. Can be left out.
|
||||
* `author`: Authorship info. Also simple. Can be left out.
|
||||
|
||||
### The `tilemap` section
|
||||
|
||||
Numeric tile IDs are not guaranteed to be consistent between different versions of the game. As such, each map file that contains grids must also contain a `tilemap` section, mapping tile definition names to the tile IDs used to encode them in the `grids` section.
|
||||
|
||||
The section is a map of numeric ID: tile definition name.
|
||||
|
||||
### The `grids` Section
|
||||
|
||||
Contains data for all the grids. The section is an ordered sequence. Each sequence is made up of a single grid's data. That data:
|
||||
@@ -49,6 +55,8 @@ Tile data is a binary array of the tile data of a chunk. Tiles are ordered witho
|
||||
|
||||
Tiles are 4 bytes in size (`ushort` for Tile ID, `ushort` for tile metadata field, little endian) Thus, since the amount of tiles is equal to `chunksize * chunksize`, the tile data per chunk is exactly `chunksize * chunksize * 4` bytes long.
|
||||
|
||||
Tile IDs should be mapped to tile definitions via the `tilemap` section.
|
||||
|
||||
## Blueprints
|
||||
|
||||
Blueprints are just map files with one grid.
|
||||
|
||||
Reference in New Issue
Block a user