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 BarryNorfolk
parent 693e29c0f2
commit 83b2bae724
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); pEast = Transform.WithEntityId(pEast, MapData.Grid);
pWest = Transform.WithEntityId(pWest, MapData.Grid); pWest = Transform.WithEntityId(pWest, MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pNorth), MapData.Grid); await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pNorth), MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pSouth), MapData.Grid); await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pSouth), MapData.Grid);
await SetTile(Plating, SEntMan.GetNetCoordinates(pEast), MapData.Grid); await SetTile(PlatingRCD, SEntMan.GetNetCoordinates(pEast), MapData.Grid);
await SetTile(Lattice, SEntMan.GetNetCoordinates(pWest), MapData.Grid); await SetTile(Lattice, SEntMan.GetNetCoordinates(pWest), MapData.Grid);
Assert.That(ProtoMan.TryIndex(RCDSettingWall, out var settingWall), $"RCDPrototype not found: {RCDSettingWall}."); 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. // Deconstruct the steel tile.
await Interact(null, pEast); await Interact(null, pEast);
await RunSeconds(settingDeconstructTile.Delay + 1); // wait for the deconstruction to finish 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. // Check that the cost of the deconstruction was subtracted from the current charges.
newCharges = sCharges.GetCurrentCharges(ToServer(rcd)); newCharges = sCharges.GetCurrentCharges(ToServer(rcd));

View File

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

View File

@ -44,6 +44,12 @@ public sealed partial class RCDPrototype : IPrototype
[DataField, ViewVariables(VVAccess.ReadOnly)] [DataField, ViewVariables(VVAccess.ReadOnly)]
public string? Prototype { get; private set; } 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> /// <summary>
/// Number of charges consumed when the operation is completed /// Number of charges consumed when the operation is completed
/// </summary> /// </summary>

View File

@ -146,7 +146,7 @@ public sealed class RCDSystem : EntitySystem
var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location); var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location);
var position = _mapSystem.TileIndicesFor(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; return;
if (!_net.IsServer) if (!_net.IsServer)
@ -254,7 +254,7 @@ public sealed class RCDSystem : EntitySystem
var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location); var tile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location);
var position = _mapSystem.TileIndicesFor(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(); args.Cancel();
} }
@ -284,7 +284,7 @@ public sealed class RCDSystem : EntitySystem
var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location); var position = _mapSystem.TileIndicesFor(gridUid.Value, mapGrid, location);
// Ensure the RCD operation is still valid // 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; return;
// Finalize the operation (this should handle prediction properly) // Finalize the operation (this should handle prediction properly)
@ -319,6 +319,11 @@ public sealed class RCDSystem : EntitySystem
#region Entity construction/deconstruction rule checks #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) 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); var prototype = _protoManager.Index(component.ProtoId);
@ -355,7 +360,7 @@ public sealed class RCDSystem : EntitySystem
{ {
case RcdMode.ConstructTile: case RcdMode.ConstructTile:
case RcdMode.ConstructObject: 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: case RcdMode.Deconstruct:
return IsDeconstructionStillValid(uid, tile, target, user, popMsgs); return IsDeconstructionStillValid(uid, tile, target, user, popMsgs);
} }
@ -363,7 +368,7 @@ public sealed class RCDSystem : EntitySystem
return false; 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); var prototype = _protoManager.Index(component.ProtoId);
@ -406,8 +411,24 @@ public sealed class RCDSystem : EntitySystem
return false; 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 // Check rule: Tiles can't be identical
if (_turf.GetContentTileDefinition(tile).ID == prototype.Prototype) if (tileDef.ID == prototype.Prototype)
{ {
if (popMsgs) if (popMsgs)
_popup.PopupClient(Loc.GetString("rcd-component-cannot-build-identical-tile"), uid, user); _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) 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)) if (isWindow && HasComp<SharedCanBuildWindowOnTopComponent>(ent))
continue; continue;
@ -534,7 +577,10 @@ public sealed class RCDSystem : EntitySystem
switch (prototype.Mode) switch (prototype.Mode)
{ {
case RcdMode.ConstructTile: 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}"); _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {gridUid} {position} to {prototype.Prototype}");
break; 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-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-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-tile = That tile already exists there!
rcd-component-cannot-build-identical-entity = That already exists there!
### Category names ### Category names

View File

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

View File

@ -22,6 +22,8 @@
- FloorPlanetGrass - FloorPlanetGrass
- FloorSnow - FloorSnow
- FloorDirt - FloorDirt
- PlatingRCD
- FloorHullReinforced
- type: tile - type: tile
id: FloorSteel id: FloorSteel
@ -1607,17 +1609,6 @@
collection: FootstepHull collection: FootstepHull
itemDrop: FloorTileItemSteel #probably should not be normally obtainable, but the game shits itself and dies when you try to put null here 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 - type: tile
id: FloorReinforcedHardened id: FloorReinforcedHardened
parent: BaseStationTile parent: BaseStationTile

View File

@ -17,6 +17,34 @@
name: tiles-plating name: tiles-plating
sprite: /Textures/_DV/Tiles/plating.png #Delta V - Resprite Tile sprite: /Textures/_DV/Tiles/plating.png #Delta V - Resprite Tile
- 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 - type: tile
id: PlatingDamaged id: PlatingDamaged
parent: BasePlating parent: BasePlating