mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-02-14 23:14:45 +01:00
Revert "Allow station tiles to be placed on solid ground and other platings. (#38898)"
This reverts commit 418b2b70b0.
Maintainer vote decided to vote this out due to code quality issues. The original contributor is aware and will get help from a maintainer to reintroduce it
This commit is contained in:
@@ -194,7 +194,7 @@ public sealed class RCDTest : InteractionTest
|
||||
// Deconstruct the steel tile.
|
||||
await Interact(null, pEast);
|
||||
await RunSeconds(settingDeconstructTile.Delay + 1); // wait for the deconstruction to finish
|
||||
await AssertTile(Plating, FromServer(pEast));
|
||||
await AssertTile(Lattice, FromServer(pEast));
|
||||
|
||||
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||
|
||||
@@ -12,7 +12,6 @@ public abstract partial class InteractionTest
|
||||
protected const string FloorItem = "FloorTileItemSteel";
|
||||
protected const string Plating = "Plating";
|
||||
protected const string Lattice = "Lattice";
|
||||
protected const string PlatingBrass = "PlatingBrass";
|
||||
|
||||
// Structures
|
||||
protected const string Airlock = "Airlock";
|
||||
|
||||
@@ -100,25 +100,4 @@ public sealed class TileConstructionTests : InteractionTest
|
||||
|
||||
await AssertEntityLookup((FloorItem, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test brassPlating -> floor -> brassPlating using tilestacking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task BrassPlatingPlace()
|
||||
{
|
||||
await SetTile(PlatingBrass);
|
||||
|
||||
// Brass Plating -> Tile
|
||||
await InteractUsing(FloorItem);
|
||||
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||
await AssertTile(Floor);
|
||||
AssertGridCount(1);
|
||||
|
||||
// Tile -> Brass Plating
|
||||
await InteractUsing(Pry);
|
||||
await AssertTile(PlatingBrass);
|
||||
AssertGridCount(1);
|
||||
await AssertEntityLookup((FloorItem, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Tiles;
|
||||
|
||||
public sealed class TileStackRecursionTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestBaseTurfRecursion()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var protoMan = pair.Server.ResolveDependency<IPrototypeManager>();
|
||||
var cfg = pair.Server.ResolveDependency<IConfigurationManager>();
|
||||
var maxTileHistoryLength = cfg.GetCVar(CCVars.TileStackLimit);
|
||||
Assert.That(protoMan.TryGetInstances<ContentTileDefinition>(out var tiles));
|
||||
Assert.That(tiles, Is.Not.EqualTo(null));
|
||||
//store the distance from the root node to the given tile node
|
||||
var nodes = new List<(ProtoId<ContentTileDefinition>, int)>();
|
||||
//each element of list is a connection from BaseTurf tile to tile that goes on it
|
||||
var edges = new List<(ProtoId<ContentTileDefinition>, ProtoId<ContentTileDefinition>)>();
|
||||
foreach (var ctdef in tiles!.Values)
|
||||
{
|
||||
//at first, each node is unexplored and has infinite distance to root.
|
||||
//we use space node as root - everything is supposed to start at space, and it's hardcoded into the game anyway.
|
||||
if (ctdef.ID == ContentTileDefinition.SpaceID)
|
||||
{
|
||||
nodes.Insert(0, (ctdef.ID, 0)); //space is the first element
|
||||
continue;
|
||||
}
|
||||
Assert.That(ctdef.BaseTurf != ctdef.ID);
|
||||
nodes.Add((ctdef.ID, int.MaxValue));
|
||||
if (ctdef.BaseTurf != null)
|
||||
edges.Add((ctdef.BaseTurf.Value, ctdef.ID));
|
||||
Assert.That(ctdef.BaseWhitelist, Does.Not.Contain(ctdef.ID));
|
||||
edges.AddRange(ctdef.BaseWhitelist.Select(possibleTurf =>
|
||||
(possibleTurf, new ProtoId<ContentTileDefinition>(ctdef.ID))));
|
||||
}
|
||||
Bfs(nodes, edges, maxTileHistoryLength);
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
private void Bfs(List<(ProtoId<ContentTileDefinition>, int)> nodes, List<(ProtoId<ContentTileDefinition>, ProtoId<ContentTileDefinition>)> edges, int depthLimit)
|
||||
{
|
||||
var root = nodes[0];
|
||||
var queue = new Queue<(ProtoId<ContentTileDefinition>, int)>();
|
||||
queue.Enqueue(root);
|
||||
while (queue.Count != 0)
|
||||
{
|
||||
var u = queue.Dequeue();
|
||||
//get a list of tiles that can be put on this tile
|
||||
var adj = edges.Where(n => n.Item1 == u.Item1).Select(n => n.Item2);
|
||||
var adjNodes = nodes.Where(n => adj.Contains(n.Item1)).ToList();
|
||||
foreach (var node in adjNodes)
|
||||
{
|
||||
var adjNode = node;
|
||||
adjNode.Item2 = u.Item2 + 1;
|
||||
Assert.That(adjNode.Item2, Is.LessThanOrEqualTo(depthLimit)); //we can doomstack tiles on top of each other. Bad!
|
||||
queue.Enqueue(adjNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,39 +517,17 @@ public sealed partial class ExplosionSystem
|
||||
else if (tileDef.MapAtmosphere)
|
||||
canCreateVacuum = true; // is already a vacuum.
|
||||
|
||||
var history = CompOrNull<TileHistoryComponent>(tileRef.GridUid);
|
||||
|
||||
// break the tile into its underlying parts
|
||||
int tileBreakages = 0;
|
||||
while (maxTileBreak > tileBreakages && _robustRandom.Prob(type.TileBreakChance(effectiveIntensity)))
|
||||
{
|
||||
tileBreakages++;
|
||||
effectiveIntensity -= type.TileBreakRerollReduction;
|
||||
|
||||
ContentTileDefinition? newDef = null;
|
||||
// does this have a base-turf that we can break it down to?
|
||||
if (string.IsNullOrEmpty(tileDef.BaseTurf))
|
||||
break;
|
||||
|
||||
// if we have tile history, we revert the tile to its previous state
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(tileRef.GridIndices, TileSystem.ChunkSize);
|
||||
if (history != null && history.ChunkHistory.TryGetValue(chunkIndices, out var chunk) &&
|
||||
chunk.History.TryGetValue(tileRef.GridIndices, out var stack) && stack.Count > 0)
|
||||
{
|
||||
// last entry in the stack
|
||||
var newId = stack[^1];
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
if (stack.Count == 0)
|
||||
chunk.History.Remove(tileRef.GridIndices);
|
||||
|
||||
Dirty(tileRef.GridUid, history);
|
||||
|
||||
newDef = (ContentTileDefinition) _tileDefinitionManager[newId.Id];
|
||||
}
|
||||
else if (tileDef.BaseTurf.HasValue)
|
||||
{
|
||||
// otherwise, we just use the base turf
|
||||
newDef = (ContentTileDefinition) _tileDefinitionManager[tileDef.BaseTurf.Value];
|
||||
}
|
||||
|
||||
if (newDef == null)
|
||||
if (_tileDefinitionManager[tileDef.BaseTurf] is not ContentTileDefinition newDef)
|
||||
break;
|
||||
|
||||
if (newDef.MapAtmosphere && !canCreateVacuum)
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Maps;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles transferring <see cref="TileHistoryComponent"/> data when a grid is split.
|
||||
/// </summary>
|
||||
public sealed class TileGridSplitSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transfer tile history from the old grid to the new grids.
|
||||
/// </summary>
|
||||
private void OnGridSplit(ref GridSplitEvent ev)
|
||||
{
|
||||
if (!TryComp<TileHistoryComponent>(ev.Grid, out var oldHistory))
|
||||
return;
|
||||
|
||||
var oldGrid = Comp<MapGridComponent>(ev.Grid);
|
||||
|
||||
foreach (var gridUid in ev.NewGrids)
|
||||
{
|
||||
// ensure the new grid has a history component and get its grid component
|
||||
var newHistory = EnsureComp<TileHistoryComponent>(gridUid);
|
||||
var newGrid = Comp<MapGridComponent>(gridUid);
|
||||
|
||||
foreach (var tile in _maps.GetAllTiles(gridUid, newGrid))
|
||||
{
|
||||
// calculate where this tile was on the old grid
|
||||
var oldIndices = _maps.LocalToTile(ev.Grid, oldGrid, new EntityCoordinates(gridUid, new Vector2(tile.GridIndices.X + 0.5f, tile.GridIndices.Y + 0.5f)));
|
||||
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(oldIndices, TileSystem.ChunkSize);
|
||||
if (oldHistory.ChunkHistory.TryGetValue(chunkIndices, out var oldChunk) &&
|
||||
oldChunk.History.TryGetValue(oldIndices, out var history))
|
||||
{
|
||||
// now we move the history from the old grid to the new grid
|
||||
var newChunkIndices = SharedMapSystem.GetChunkIndices(tile.GridIndices, TileSystem.ChunkSize);
|
||||
if (!newHistory.ChunkHistory.TryGetValue(newChunkIndices, out var newChunk))
|
||||
{
|
||||
newChunk = new TileHistoryChunk();
|
||||
newHistory.ChunkHistory[newChunkIndices] = newChunk;
|
||||
}
|
||||
|
||||
newChunk.History[tile.GridIndices] = new List<ProtoId<ContentTileDefinition>>(history);
|
||||
newChunk.LastModified = _timing.CurTick;
|
||||
|
||||
// clean up the old history
|
||||
oldChunk.History.Remove(oldIndices);
|
||||
if (oldChunk.History.Count == 0)
|
||||
oldHistory.ChunkHistory.Remove(chunkIndices);
|
||||
else
|
||||
oldChunk.LastModified = _timing.CurTick;
|
||||
}
|
||||
}
|
||||
|
||||
Dirty(gridUid, newHistory);
|
||||
}
|
||||
|
||||
Dirty(ev.Grid, oldHistory);
|
||||
}
|
||||
}
|
||||
@@ -409,13 +409,4 @@ public sealed partial class CCVars
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> GameHostnameInTitlebar =
|
||||
CVarDef.Create("game.hostname_in_titlebar", true, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of tiles you can stack on top of each other. 0 is unlimited.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Having it too high can result in "doomstacking" tiles - this messes with efficiency of explosions, deconstruction of tiles, and might result in memory problems.
|
||||
/// </remarks>
|
||||
public static readonly CVarDef<int> TileStackLimit =
|
||||
CVarDef.Create("game.tile_stack_limit", 5, CVar.SERVER | CVar.REPLICATED);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Maps
|
||||
@@ -42,13 +41,7 @@ namespace Content.Shared.Maps
|
||||
[DataField("isSubfloor")] public bool IsSubFloor { get; private set; }
|
||||
|
||||
[DataField("baseTurf")]
|
||||
public ProtoId<ContentTileDefinition>? BaseTurf { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// On what tiles this tile can be placed on. BaseTurf is already included.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<ContentTileDefinition>> BaseWhitelist { get; private set; } = new();
|
||||
public string BaseTurf { get; private set; } = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public PrototypeFlags<ToolQualityPrototype> DeconstructTools { get; set; } = new();
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Maps;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class TileHistoryComponent : Component
|
||||
{
|
||||
// History of tiles for each grid chunk.
|
||||
[DataField]
|
||||
public Dictionary<Vector2i, TileHistoryChunk> ChunkHistory = new();
|
||||
|
||||
/// <summary>
|
||||
/// Tick at which PVS was last toggled. Ensures that all players receive a full update when toggling PVS.
|
||||
/// </summary>
|
||||
public GameTick ForceTick { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class TileHistoryState : ComponentState
|
||||
{
|
||||
public Dictionary<Vector2i, TileHistoryChunk> ChunkHistory;
|
||||
|
||||
public TileHistoryState(Dictionary<Vector2i, TileHistoryChunk> chunkHistory)
|
||||
{
|
||||
ChunkHistory = chunkHistory;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class TileHistoryDeltaState : ComponentState, IComponentDeltaState<TileHistoryState>
|
||||
{
|
||||
public Dictionary<Vector2i, TileHistoryChunk> ChunkHistory;
|
||||
public HashSet<Vector2i> AllHistoryChunks;
|
||||
|
||||
public TileHistoryDeltaState(Dictionary<Vector2i, TileHistoryChunk> chunkHistory, HashSet<Vector2i> allHistoryChunks)
|
||||
{
|
||||
ChunkHistory = chunkHistory;
|
||||
AllHistoryChunks = allHistoryChunks;
|
||||
}
|
||||
|
||||
public void ApplyToFullState(TileHistoryState state)
|
||||
{
|
||||
var toRemove = new List<Vector2i>();
|
||||
foreach (var key in state.ChunkHistory.Keys)
|
||||
{
|
||||
if (!AllHistoryChunks.Contains(key))
|
||||
toRemove.Add(key);
|
||||
}
|
||||
|
||||
foreach (var key in toRemove)
|
||||
{
|
||||
state.ChunkHistory.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (indices, chunk) in ChunkHistory)
|
||||
{
|
||||
state.ChunkHistory[indices] = new TileHistoryChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyToComponent(TileHistoryComponent component)
|
||||
{
|
||||
var toRemove = new List<Vector2i>();
|
||||
foreach (var key in component.ChunkHistory.Keys)
|
||||
{
|
||||
if (!AllHistoryChunks.Contains(key))
|
||||
toRemove.Add(key);
|
||||
}
|
||||
|
||||
foreach (var key in toRemove)
|
||||
{
|
||||
component.ChunkHistory.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (indices, chunk) in ChunkHistory)
|
||||
{
|
||||
component.ChunkHistory[indices] = new TileHistoryChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public TileHistoryState CreateNewFullState(TileHistoryState state)
|
||||
{
|
||||
var chunks = new Dictionary<Vector2i, TileHistoryChunk>(state.ChunkHistory.Count);
|
||||
|
||||
foreach (var (indices, chunk) in ChunkHistory)
|
||||
{
|
||||
chunks[indices] = new TileHistoryChunk(chunk);
|
||||
}
|
||||
|
||||
foreach (var (indices, chunk) in state.ChunkHistory)
|
||||
{
|
||||
if (AllHistoryChunks.Contains(indices))
|
||||
chunks.TryAdd(indices, new TileHistoryChunk(chunk));
|
||||
}
|
||||
|
||||
return new TileHistoryState(chunks);
|
||||
}
|
||||
}
|
||||
|
||||
[DataDefinition, Serializable, NetSerializable]
|
||||
public sealed partial class TileHistoryChunk
|
||||
{
|
||||
[DataField]
|
||||
public Dictionary<Vector2i, List<ProtoId<ContentTileDefinition>>> History = new();
|
||||
|
||||
[ViewVariables]
|
||||
public GameTick LastModified;
|
||||
|
||||
public TileHistoryChunk()
|
||||
{
|
||||
}
|
||||
|
||||
public TileHistoryChunk(TileHistoryChunk other)
|
||||
{
|
||||
History = new Dictionary<Vector2i, List<ProtoId<ContentTileDefinition>>>(other.History.Count);
|
||||
foreach (var (key, value) in other.History)
|
||||
{
|
||||
History[key] = new List<ProtoId<ContentTileDefinition>>(value);
|
||||
}
|
||||
LastModified = other.LastModified;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Tiles;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Maps;
|
||||
@@ -20,85 +14,12 @@ namespace Content.Shared.Maps;
|
||||
/// </summary>
|
||||
public sealed class TileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly SharedDecalSystem _decal = default!;
|
||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public const int ChunkSize = 16;
|
||||
|
||||
private int _tileStackLimit;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridStartup);
|
||||
SubscribeLocalEvent<TileHistoryComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<TileHistoryComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<TileHistoryComponent, FloorTileAttemptEvent>(OnFloorTileAttempt);
|
||||
|
||||
_cfg.OnValueChanged(CCVars.TileStackLimit, t => _tileStackLimit = t, true);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, TileHistoryComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not TileHistoryState state && args.Current is not TileHistoryDeltaState)
|
||||
return;
|
||||
|
||||
if (args.Current is TileHistoryState fullState)
|
||||
{
|
||||
component.ChunkHistory.Clear();
|
||||
foreach (var (key, value) in fullState.ChunkHistory)
|
||||
{
|
||||
component.ChunkHistory[key] = new TileHistoryChunk(value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Current is TileHistoryDeltaState deltaState)
|
||||
{
|
||||
deltaState.ApplyToComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, TileHistoryComponent component, ref ComponentGetState args)
|
||||
{
|
||||
if (args.FromTick <= component.CreationTick || args.FromTick <= component.ForceTick)
|
||||
{
|
||||
var fullHistory = new Dictionary<Vector2i, TileHistoryChunk>(component.ChunkHistory.Count);
|
||||
foreach (var (key, value) in component.ChunkHistory)
|
||||
{
|
||||
fullHistory[key] = new TileHistoryChunk(value);
|
||||
}
|
||||
args.State = new TileHistoryState(fullHistory);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = new Dictionary<Vector2i, TileHistoryChunk>();
|
||||
foreach (var (index, chunk) in component.ChunkHistory)
|
||||
{
|
||||
if (chunk.LastModified >= args.FromTick)
|
||||
data[index] = new TileHistoryChunk(chunk);
|
||||
}
|
||||
|
||||
args.State = new TileHistoryDeltaState(data, new(component.ChunkHistory.Keys));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On grid startup, ensure that we have Tile History.
|
||||
/// </summary>
|
||||
private void OnGridStartup(GridInitializeEvent ev)
|
||||
{
|
||||
if (HasComp<MapComponent>(ev.EntityUid))
|
||||
return;
|
||||
|
||||
EnsureComp<TileHistoryComponent>(ev.EntityUid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a weighted pick of a tile variant.
|
||||
@@ -164,7 +85,7 @@ public sealed class TileSystem : EntitySystem
|
||||
return PryTile(tileRef);
|
||||
}
|
||||
|
||||
public bool PryTile(TileRef tileRef)
|
||||
public bool PryTile(TileRef tileRef)
|
||||
{
|
||||
return PryTile(tileRef, false);
|
||||
}
|
||||
@@ -176,7 +97,7 @@ public sealed class TileSystem : EntitySystem
|
||||
if (tile.IsEmpty)
|
||||
return false;
|
||||
|
||||
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.TypeId];
|
||||
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.TypeId];
|
||||
|
||||
if (!tileDef.CanCrowbar)
|
||||
return false;
|
||||
@@ -191,73 +112,33 @@ public sealed class TileSystem : EntitySystem
|
||||
return ReplaceTile(tileref, replacementTile, tileref.GridUid, grid);
|
||||
}
|
||||
|
||||
public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile, EntityUid grid, MapGridComponent? component = null, byte? variant = null)
|
||||
public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile, EntityUid grid, MapGridComponent? component = null)
|
||||
{
|
||||
DebugTools.Assert(tileref.GridUid == grid);
|
||||
|
||||
if (!Resolve(grid, ref component))
|
||||
return false;
|
||||
|
||||
var key = tileref.GridIndices;
|
||||
var currentTileDef = (ContentTileDefinition) _tileDefinitionManager[tileref.Tile.TypeId];
|
||||
|
||||
// If the tile we're placing has a baseTurf that matches the tile we're replacing, we don't need to create a history
|
||||
// unless the tile already has a history.
|
||||
var history = EnsureComp<TileHistoryComponent>(grid);
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(key, ChunkSize);
|
||||
history.ChunkHistory.TryGetValue(chunkIndices, out var chunk);
|
||||
var historyExists = chunk != null && chunk.History.ContainsKey(key);
|
||||
|
||||
if (replacementTile.BaseTurf != currentTileDef.ID || historyExists)
|
||||
{
|
||||
if (chunk == null)
|
||||
{
|
||||
chunk = new TileHistoryChunk();
|
||||
history.ChunkHistory[chunkIndices] = chunk;
|
||||
}
|
||||
|
||||
chunk.LastModified = _timing.CurTick;
|
||||
Dirty(grid, history);
|
||||
|
||||
//Create stack if needed
|
||||
if (!chunk.History.TryGetValue(key, out var stack))
|
||||
{
|
||||
stack = new List<ProtoId<ContentTileDefinition>>();
|
||||
chunk.History[key] = stack;
|
||||
}
|
||||
|
||||
//Prevent the doomstack
|
||||
if (stack.Count >= _tileStackLimit && _tileStackLimit != 0)
|
||||
return false;
|
||||
|
||||
//Push current tile to the stack, if not empty
|
||||
if (!tileref.Tile.IsEmpty)
|
||||
{
|
||||
stack.Add(currentTileDef.ID);
|
||||
}
|
||||
}
|
||||
|
||||
variant ??= PickVariant(replacementTile);
|
||||
var variant = PickVariant(replacementTile);
|
||||
var decals = _decal.GetDecalsInRange(tileref.GridUid, _turf.GetTileCenter(tileref).Position, 0.5f);
|
||||
foreach (var (id, _) in decals)
|
||||
{
|
||||
_decal.RemoveDecal(tileref.GridUid, id);
|
||||
}
|
||||
|
||||
_maps.SetTile(grid, component, tileref.GridIndices, new Tile(replacementTile.TileId, 0, variant.Value));
|
||||
_maps.SetTile(grid, component, tileref.GridIndices, new Tile(replacementTile.TileId, 0, variant));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool DeconstructTile(TileRef tileRef, bool spawnItem = true)
|
||||
public bool DeconstructTile(TileRef tileRef)
|
||||
{
|
||||
if (tileRef.Tile.IsEmpty)
|
||||
return false;
|
||||
|
||||
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tileRef.Tile.TypeId];
|
||||
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
|
||||
|
||||
//Can't deconstruct anything that doesn't have a base turf.
|
||||
if (tileDef.BaseTurf == null)
|
||||
if (string.IsNullOrEmpty(tileDef.BaseTurf))
|
||||
return false;
|
||||
|
||||
var gridUid = tileRef.GridUid;
|
||||
@@ -271,68 +152,20 @@ public sealed class TileSystem : EntitySystem
|
||||
(_robustRandom.NextFloat() - 0.5f) * bounds,
|
||||
(_robustRandom.NextFloat() - 0.5f) * bounds));
|
||||
|
||||
var historyComp = EnsureComp<TileHistoryComponent>(gridUid);
|
||||
ProtoId<ContentTileDefinition> previousTileId;
|
||||
//Actually spawn the relevant tile item at the right position and give it some random offset.
|
||||
var tileItem = Spawn(tileDef.ItemDropPrototypeName, coordinates);
|
||||
Transform(tileItem).LocalRotation = _robustRandom.NextDouble() * Math.Tau;
|
||||
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(indices, ChunkSize);
|
||||
|
||||
//Pop from stack if we have history
|
||||
if (historyComp.ChunkHistory.TryGetValue(chunkIndices, out var chunk) &&
|
||||
chunk.History.TryGetValue(indices, out var stack) && stack.Count > 0)
|
||||
{
|
||||
chunk.LastModified = _timing.CurTick;
|
||||
Dirty(gridUid, historyComp);
|
||||
|
||||
previousTileId = stack.Last();
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
|
||||
//Clean up empty stacks to avoid memory buildup
|
||||
if (stack.Count == 0)
|
||||
{
|
||||
chunk.History.Remove(indices);
|
||||
}
|
||||
|
||||
// Clean up empty chunks
|
||||
if (chunk.History.Count == 0)
|
||||
{
|
||||
historyComp.ChunkHistory.Remove(chunkIndices);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//No stack? Assume BaseTurf was the layer below
|
||||
previousTileId = tileDef.BaseTurf.Value;
|
||||
}
|
||||
|
||||
if (spawnItem)
|
||||
{
|
||||
//Actually spawn the relevant tile item at the right position and give it some random offset.
|
||||
var tileItem = Spawn(tileDef.ItemDropPrototypeName, coordinates);
|
||||
Transform(tileItem).LocalRotation = _robustRandom.NextDouble() * Math.Tau;
|
||||
}
|
||||
|
||||
//Destroy any decals on the tile
|
||||
// Destroy any decals on the tile
|
||||
var decals = _decal.GetDecalsInRange(gridUid, coordinates.SnapToGrid(EntityManager, _mapManager).Position, 0.5f);
|
||||
foreach (var (id, _) in decals)
|
||||
{
|
||||
_decal.RemoveDecal(tileRef.GridUid, id);
|
||||
}
|
||||
|
||||
//Replace tile with the one it was placed on
|
||||
var previousDef = (ContentTileDefinition)_tileDefinitionManager[previousTileId];
|
||||
_maps.SetTile(gridUid, mapGrid, indices, new Tile(previousDef.TileId));
|
||||
var plating = _tileDefinitionManager[tileDef.BaseTurf];
|
||||
_maps.SetTile(gridUid, mapGrid, tileRef.GridIndices, new Tile(plating.TileId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnFloorTileAttempt(Entity<TileHistoryComponent> ent, ref FloorTileAttemptEvent args)
|
||||
{
|
||||
if (_tileStackLimit == 0)
|
||||
return;
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(args.GridIndices, ChunkSize);
|
||||
if (!ent.Comp.ChunkHistory.TryGetValue(chunkIndices, out var chunk) ||
|
||||
!chunk.History.TryGetValue(args.GridIndices, out var stack))
|
||||
return;
|
||||
args.Cancelled = stack.Count >= _tileStackLimit; // greater or equals because the attempt itself counts as a tile we're trying to place
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ public sealed class RCDSystem : EntitySystem
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly TileSystem _tile = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
@@ -561,9 +560,10 @@ public sealed class RCDSystem : EntitySystem
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
// Deconstruct tile, don't drop tile as item
|
||||
if (_tile.DeconstructTile(tile, spawnItem: false))
|
||||
_adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {gridUid} tile: {position} open to space");
|
||||
// Deconstruct tile (either converts the tile to lattice, or removes lattice)
|
||||
var tileDef = (_turf.GetContentTileDefinition(tile).ID != "Lattice") ? new Tile(_tileDefMan["Lattice"].TileId) : Tile.Empty;
|
||||
_mapSystem.SetTile(gridUid, mapGrid, position, tileDef);
|
||||
_adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {gridUid} tile: {position} open to space");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -16,7 +16,6 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Tiles;
|
||||
@@ -143,7 +142,7 @@ public sealed class FloorTileSystem : EntitySystem
|
||||
|
||||
var baseTurf = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
|
||||
|
||||
if (CanPlaceOn(currentTileDefinition, baseTurf.ID))
|
||||
if (HasBaseTurf(currentTileDefinition, baseTurf.ID))
|
||||
{
|
||||
if (!_stackSystem.TryUse((uid, stack), 1))
|
||||
continue;
|
||||
@@ -153,7 +152,7 @@ public sealed class FloorTileSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (HasBaseTurf(currentTileDefinition, new ProtoId<ContentTileDefinition>(ContentTileDefinition.SpaceID)))
|
||||
else if (HasBaseTurf(currentTileDefinition, ContentTileDefinition.SpaceID))
|
||||
{
|
||||
if (!_stackSystem.TryUse((uid, stack), 1))
|
||||
continue;
|
||||
@@ -172,35 +171,19 @@ public sealed class FloorTileSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasBaseTurf(ContentTileDefinition tileDef, ProtoId<ContentTileDefinition> baseTurf)
|
||||
public bool HasBaseTurf(ContentTileDefinition tileDef, string baseTurf)
|
||||
{
|
||||
return tileDef.BaseTurf == baseTurf;
|
||||
}
|
||||
|
||||
private bool CanPlaceOn(ContentTileDefinition tileDef, ProtoId<ContentTileDefinition> currentTurfId)
|
||||
{
|
||||
//Check exact BaseTurf match
|
||||
if (tileDef.BaseTurf == currentTurfId)
|
||||
return true;
|
||||
|
||||
// Check whitelist match
|
||||
if (tileDef.BaseWhitelist.Count > 0 && tileDef.BaseWhitelist.Contains(currentTurfId))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PlaceAt(EntityUid user, EntityUid gridUid, MapGridComponent mapGrid, EntityCoordinates location,
|
||||
ushort tileId, SoundSpecifier placeSound, float offset = 0)
|
||||
{
|
||||
_adminLogger.Add(LogType.Tile, LogImpact.Low, $"{ToPrettyString(user):actor} placed tile {_tileDefinitionManager[tileId].Name} at {ToPrettyString(gridUid)} {location}");
|
||||
|
||||
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileId];
|
||||
var random = new System.Random((int)_timing.CurTick.Value);
|
||||
var variant = _tile.PickVariant(tileDef, random);
|
||||
|
||||
var tileRef = _map.GetTileRef(gridUid, mapGrid, location.Offset(new Vector2(offset, offset)));
|
||||
_tile.ReplaceTile(tileRef, tileDef, gridUid, mapGrid, variant: variant);
|
||||
var random = new System.Random((int) _timing.CurTick.Value);
|
||||
var variant = _tile.PickVariant((ContentTileDefinition) _tileDefinitionManager[tileId], random);
|
||||
_map.SetTile(gridUid, mapGrid,location.Offset(new Vector2(offset, offset)), new Tile(tileId, 0, variant));
|
||||
|
||||
_audio.PlayPredicted(placeSound, location, user);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,5 @@
|
||||
- type: tile
|
||||
id: BaseFloorPlanet
|
||||
abstract: true
|
||||
heatCapacity: 10000
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepAsteroid
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
- type: tile
|
||||
id: FloorPlanetDirt
|
||||
parent: BaseFloorPlanet
|
||||
name: tiles-dirt-planet-floor
|
||||
sprite: /Textures/Tiles/Planet/dirt.rsi/dirt.png
|
||||
variants: 4
|
||||
@@ -19,11 +8,16 @@
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepAsteroid
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Desert
|
||||
- type: tile
|
||||
id: FloorDesert
|
||||
parent: BaseFloorPlanet
|
||||
name: tiles-desert-floor
|
||||
sprite: /Textures/Tiles/Planet/Desert/desert.png
|
||||
variants: 6
|
||||
@@ -34,6 +28,12 @@
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepAsteroid
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
- type: tile
|
||||
id: FloorLowDesert
|
||||
@@ -47,11 +47,16 @@
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepAsteroid
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Grass
|
||||
- type: tile
|
||||
id: FloorPlanetGrass
|
||||
parent: BaseFloorPlanet
|
||||
name: tiles-grass-planet-floor
|
||||
sprite: /Textures/Tiles/Planet/Grass/grass.png
|
||||
variants: 4
|
||||
@@ -71,20 +76,29 @@
|
||||
North: /Textures/Tiles/Planet/Grass/double_edge.png
|
||||
West: /Textures/Tiles/Planet/Grass/double_edge.png
|
||||
baseTurf: FloorPlanetDirt
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepGrass
|
||||
itemDrop: FloorTileItemGrass
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Lava
|
||||
- type: tile
|
||||
id: FloorBasalt
|
||||
name: tiles-basalt-floor
|
||||
parent: BaseFloorPlanet
|
||||
sprite: /Textures/Tiles/Planet/basalt.png
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepAsteroid
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Snow
|
||||
- type: tile
|
||||
id: FloorSnow
|
||||
parent: BaseFloorPlanet
|
||||
name: tiles-snow
|
||||
sprite: /Textures/Tiles/Planet/Snow/snow.png
|
||||
variants: 13
|
||||
@@ -108,8 +122,12 @@
|
||||
East: /Textures/Tiles/Planet/Snow/snow_double_edge_east.png
|
||||
North: /Textures/Tiles/Planet/Snow/snow_double_edge_north.png
|
||||
West: /Textures/Tiles/Planet/Snow/snow_double_edge_west.png
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepSnow
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Ice
|
||||
- type: tile
|
||||
@@ -126,7 +144,6 @@
|
||||
# Dug snow
|
||||
- type: tile
|
||||
id: FloorSnowDug
|
||||
parent: BaseFloorPlanet
|
||||
name: tiles-snow-dug
|
||||
sprite: /Textures/Tiles/Planet/Snow/snow_dug.png
|
||||
edgeSpritePriority: 1
|
||||
@@ -135,7 +152,11 @@
|
||||
East: /Textures/Tiles/Planet/Snow/snow_dug_double_edge_east.png
|
||||
North: /Textures/Tiles/Planet/Snow/snow_dug_double_edge_north.png
|
||||
West: /Textures/Tiles/Planet/Snow/snow_dug_double_edge_west.png
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepSnow
|
||||
heatCapacity: 10000
|
||||
weather: true
|
||||
indestructible: true
|
||||
|
||||
# Wasteland
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
- type: tile
|
||||
id: BasePlating
|
||||
abstract: true
|
||||
friction: 1.5
|
||||
heatCapacity: 10000
|
||||
id: Plating
|
||||
name: tiles-plating
|
||||
sprite: /Textures/Tiles/plating.png
|
||||
baseTurf: Lattice
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
baseTurf: Lattice
|
||||
baseWhitelist:
|
||||
- TrainLattice
|
||||
|
||||
- type: tile
|
||||
id: Plating
|
||||
parent: BasePlating
|
||||
name: tiles-plating
|
||||
sprite: /Textures/Tiles/plating.png
|
||||
friction: 1.5
|
||||
heatCapacity: 10000
|
||||
|
||||
- type: tile
|
||||
id: PlatingDamaged
|
||||
parent: BasePlating
|
||||
name: tiles-plating
|
||||
sprite: /Textures/Tiles/plating_damaged.png
|
||||
variants: 3
|
||||
@@ -26,25 +18,45 @@
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
baseTurf: Lattice
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
friction: 1.5
|
||||
heatCapacity: 10000
|
||||
|
||||
- type: tile
|
||||
id: PlatingAsteroid
|
||||
parent: BasePlating
|
||||
name: tiles-asteroid-plating
|
||||
sprite: /Textures/Tiles/Asteroid/asteroid_plating.png
|
||||
baseTurf: Lattice
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
friction: 1.5
|
||||
heatCapacity: 10000
|
||||
|
||||
- type: tile
|
||||
id: PlatingBrass
|
||||
parent: BasePlating
|
||||
name: tiles-brass-plating
|
||||
sprite: /Textures/Tiles/Misc/clockwork/clockwork_floor.png
|
||||
baseTurf: Lattice
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
friction: 1.5
|
||||
heatCapacity: 10000
|
||||
|
||||
- type: tile
|
||||
id: PlatingSnow
|
||||
name: tiles-snow-plating
|
||||
parent: BasePlating
|
||||
sprite: /Textures/Tiles/snow_plating.png #Not in the snow planet RSI because it doesn't have any metadata. Should probably be moved to its own folder later.
|
||||
baseTurf: Lattice
|
||||
isSubfloor: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
friction: 0.75 #a little less then actual snow
|
||||
heatCapacity: 10000
|
||||
|
||||
- type: tile
|
||||
id: PlatingIronsand
|
||||
@@ -75,8 +87,16 @@
|
||||
|
||||
- type: tile
|
||||
id: TrainLattice
|
||||
parent: Lattice
|
||||
name: tiles-lattice-train
|
||||
sprite: /Textures/Tiles/latticeTrain.png
|
||||
baseTurf: Space
|
||||
isSubfloor: true
|
||||
deconstructTools: [ Cutting ]
|
||||
weather: true
|
||||
footstepSounds:
|
||||
collection: FootstepPlating
|
||||
friction: 1.5
|
||||
isSpace: true
|
||||
itemDrop: PartRodMetal1
|
||||
heatCapacity: 10000
|
||||
mass: 200
|
||||
|
||||
Reference in New Issue
Block a user