Fix RCD light spam, bypass of indestructible tiles and some plating fixes (#42432)

* No more light spam, and some plating fixes

* fixed test
This commit is contained in:
Velken
2026-01-15 17:22:54 -03:00
committed by GitHub
parent cd6c521b37
commit 7d58e42ade
8 changed files with 100 additions and 23 deletions

View File

@@ -38,9 +38,9 @@ public sealed class RCDTest : InteractionTest
pEast = Transform.WithEntityId(pEast, MapData.Grid);
pWest = Transform.WithEntityId(pWest, MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pNorth), MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pSouth), MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pEast), MapData.Grid);
await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pNorth), MapData.Grid);
await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pSouth), MapData.Grid);
await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pEast), MapData.Grid);
await SetTile(Lattice, SEntMan.GetNetCoordinates(pWest), MapData.Grid);
Assert.That(ProtoMan.TryIndex(RCDSettingWall, out var settingWall), $"RCDPrototype not found: {RCDSettingWall}.");
@@ -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(PlatingRCD, FromServer(pEast));
// Check that the cost of the deconstruction was subtracted from the current charges.
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));

View File

@@ -11,6 +11,7 @@ public abstract partial class InteractionTest
protected const string Floor = "FloorSteel";
protected const string FloorItem = "FloorTileItemSteel";
protected const string Plating = "Plating";
protected const string PlatingRCD = "PlatingRCD";
protected const string Lattice = "Lattice";
protected const string PlatingBrass = "PlatingBrass";

View File

@@ -44,6 +44,12 @@ public sealed partial class RCDPrototype : IPrototype
[DataField, ViewVariables(VVAccess.ReadOnly)]
public string? Prototype { get; private set; }
/// <summary>
/// If true, allows placing the entity once per direction (North, West, South and East)
/// </summary>
[DataField, ViewVariables(VVAccess.ReadOnly)]
public bool AllowMultiDirection { get; private set; }
/// <summary>
/// Number of charges consumed when the operation is completed
/// </summary>

View File

@@ -146,7 +146,7 @@ public sealed class RCDSystem : EntitySystem
var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location);
var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location);
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, args.Target, args.User))
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, component.ConstructionDirection, args.Target, args.User))
return;
if (!_net.IsServer)
@@ -254,7 +254,7 @@ public sealed class RCDSystem : EntitySystem
var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location);
var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location);
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, args.Event.Target, args.Event.User))
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, args.Event.Direction, args.Event.Target, args.Event.User))
args.Cancel();
}
@@ -284,7 +284,7 @@ public sealed class RCDSystem : EntitySystem
var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location);
// Ensure the RCD operation is still valid
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, args.Target, args.User))
if (!IsRCDOperationStillValid(uid, component, gridUid.Value, mapGrid, tile, position, args.Direction, args.Target, args.User))
return;
// Finalize the operation (this should handle prediction properly)
@@ -319,6 +319,11 @@ public sealed class RCDSystem : EntitySystem
#region Entity construction/deconstruction rule checks
public bool IsRCDOperationStillValid(EntityUid uid, RCDComponent component, EntityUid gridUid, MapGridComponent mapGrid, TileRef tile, Vector2i position, EntityUid? target, EntityUid user, bool popMsgs = true)
{
return IsRCDOperationStillValid(uid, component, gridUid, mapGrid, tile, position, component.ConstructionDirection, target, user, popMsgs);
}
public bool IsRCDOperationStillValid(EntityUid uid, RCDComponent component, EntityUid gridUid, MapGridComponent mapGrid, TileRef tile, Vector2i position, Direction direction, EntityUid? target, EntityUid user, bool popMsgs = true)
{
var prototype = _protoManager.Index(component.ProtoId);
@@ -355,7 +360,7 @@ public sealed class RCDSystem : EntitySystem
{
case RcdMode.ConstructTile:
case RcdMode.ConstructObject:
return IsConstructionLocationValid(uid, component, gridUid, mapGrid, tile, position, user, popMsgs);
return IsConstructionLocationValid(uid, component, gridUid, mapGrid, tile, position, direction, user, popMsgs);
case RcdMode.Deconstruct:
return IsDeconstructionStillValid(uid, tile, target, user, popMsgs);
}
@@ -363,7 +368,7 @@ public sealed class RCDSystem : EntitySystem
return false;
}
private bool IsConstructionLocationValid(EntityUid uid, RCDComponent component, EntityUid gridUid, MapGridComponent mapGrid, TileRef tile, Vector2i position, EntityUid user, bool popMsgs = true)
private bool IsConstructionLocationValid(EntityUid uid, RCDComponent component, EntityUid gridUid, MapGridComponent mapGrid, TileRef tile, Vector2i position, Direction direction, EntityUid user, bool popMsgs = true)
{
var prototype = _protoManager.Index(component.ProtoId);
@@ -406,8 +411,24 @@ public sealed class RCDSystem : EntitySystem
return false;
}
var tileDef = _turf.GetContentTileDefinition(tile);
// Check rule: Respect baseTurf and baseWhitelist
if (prototype.Prototype != null && _tileDefMan.TryGetDefinition(prototype.Prototype, out var replacementDef))
{
var replacementContentDef = (ContentTileDefinition) replacementDef;
if (replacementContentDef.BaseTurf != tileDef.ID && !replacementContentDef.BaseWhitelist.Contains(tileDef.ID))
{
if (popMsgs)
_popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-empty-tile-message"), uid, user);
return false;
}
}
// Check rule: Tiles can't be identical
if (_turf.GetContentTileDefinition(tile).ID == prototype.Prototype)
if (tileDef.ID == prototype.Prototype)
{
if (popMsgs)
_popup.PopupClient(Loc.GetString("rcd-component-cannot-build-identical-tile"), uid, user);
@@ -430,6 +451,28 @@ public sealed class RCDSystem : EntitySystem
foreach (var ent in _intersectingEntities)
{
// If the entity is the exact same prototype as what we are trying to build, then block it.
// This is to prevent spamming objects on the same tile (e.g. lights)
if (prototype.Prototype != null && MetaData(ent).EntityPrototype?.ID == prototype.Prototype)
{
var isIdentical = true;
if (prototype.AllowMultiDirection)
{
var entDirection = Transform(ent).LocalRotation.GetCardinalDir();
if (entDirection != direction)
isIdentical = false;
}
if (isIdentical)
{
if (popMsgs)
_popup.PopupClient(Loc.GetString("rcd-component-cannot-build-identical-entity"), uid, user);
return false;
}
}
if (isWindow && HasComp<SharedCanBuildWindowOnTopComponent>(ent))
continue;
@@ -534,7 +577,10 @@ public sealed class RCDSystem : EntitySystem
switch (prototype.Mode)
{
case RcdMode.ConstructTile:
_mapSystem.SetTile(gridUid, mapGrid, position, new Tile(_tileDefMan[prototype.Prototype].TileId));
if (!_tileDefMan.TryGetDefinition(prototype.Prototype, out var tileDef))
return;
_tile.ReplaceTile(tile, (ContentTileDefinition) tileDef, gridUid, mapGrid);
_adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {gridUid} {position} to {prototype.Prototype}");
break;

View File

@@ -29,6 +29,7 @@ rcd-component-must-build-on-subfloor-message = You can only build that on expose
rcd-component-cannot-build-on-subfloor-message = You can't build that on exposed subfloor!
rcd-component-cannot-build-on-occupied-tile-message = You can't build here, the space is already occupied!
rcd-component-cannot-build-identical-tile = That tile already exists there!
rcd-component-cannot-build-identical-entity = That already exists there!
### Category names

View File

@@ -37,7 +37,7 @@
category: WallsAndFlooring
sprite: /Textures/Interface/Radial/RCD/plating.png
mode: ConstructTile
prototype: Plating
prototype: PlatingRCD
cost: 1
delay: 1
collisionMask: InteractImpassable
@@ -128,6 +128,7 @@
- IsWindow
rotation: User
fx: EffectRCDConstruct1
allowMultiDirection: true
- type: rcd
id: ReinforcedWindow
@@ -157,6 +158,7 @@
- IsWindow
rotation: User
fx: EffectRCDConstruct2
allowMultiDirection: true
# Airlocks
- type: rcd
@@ -208,6 +210,7 @@
collisionBounds: "-0.23,-0.49,0.23,-0.36"
rotation: User
fx: EffectRCDConstruct1
allowMultiDirection: true
- type: rcd
id: BulbLight
@@ -221,6 +224,7 @@
collisionBounds: "-0.23,-0.49,0.23,-0.36"
rotation: User
fx: EffectRCDConstruct1
allowMultiDirection: true
# Electrical
- type: rcd

View File

@@ -22,6 +22,8 @@
- FloorPlanetGrass
- FloorSnow
- FloorDirt
- PlatingRCD
- FloorHullReinforced
- type: tile
id: FloorSteel
@@ -1607,17 +1609,6 @@
collection: FootstepHull
itemDrop: FloorTileItemSteel #probably should not be normally obtainable, but the game shits itself and dies when you try to put null here
- type: tile
id: FloorHullReinforced
parent: BaseStationTile
name: tiles-hull-reinforced
sprite: /Textures/Tiles/hull_reinforced.png
footstepSounds:
collection: FootstepHull
itemDrop: FloorTileItemSteel
heatCapacity: 100000 #/tg/ has this set as "INFINITY." I don't know if that exists here so I've just added an extra 0
indestructible: true
- type: tile
id: FloorReinforcedHardened
parent: BaseStationTile

View File

@@ -16,6 +16,34 @@
name: tiles-plating
sprite: /Textures/Tiles/plating.png
- type: tile
id: PlatingRCD
parent: Plating
baseWhitelist:
- TrainLattice
- FloorPlanetDirt
- FloorDesert
- FloorLowDesert
- FloorPlanetGrass
- FloorSnow
- FloorDirt
- FloorAsteroidIronsand
- FloorAsteroidSand
- FloorAsteroidSandBorderless
- FloorAsteroidIronsandBorderless
- FloorAsteroidSandRedBorderless
- type: tile
id: FloorHullReinforced
parent: BasePlating
name: tiles-hull-reinforced
sprite: /Textures/Tiles/hull_reinforced.png
footstepSounds:
collection: FootstepHull
itemDrop: FloorTileItemSteel
heatCapacity: 100000 #/tg/ has this set as "INFINITY." I don't know if that exists here so I've just added an extra 0
indestructible: true
- type: tile
id: PlatingDamaged
parent: BasePlating