Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.
Merged
2 changes: 1 addition & 1 deletion Content.Server/Bible/BibleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ private void AttemptSummon(Entity<SummonableComponent> ent, EntityUid user, Tran
_transform.SetParent(familiar, uid);
}
component.AlreadySummoned = true;
_actionsSystem.RemoveAction(user, component.SummonActionEntity);
_actionsSystem.RemoveAction(component.SummonActionEntity);
}
}
}
4 changes: 2 additions & 2 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,10 @@ public void TrySendInGameICMessage(
SendEntityEmote(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
break;
case InGameICChatType.Subtle:
SendEntitySubtle(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker, color: color);
SendEntitySubtle(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: true, color: color);
break;
case InGameICChatType.SubtleOOC:
SendEntitySubtle(source, $"OOC: {message}", range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker, color: color); // HardLight: Capitalized OOC for consistency with other OOC chats.
SendEntitySubtle(source, $"OOC: {message}", range, nameOverride, hideLog: hideLog, ignoreActionBlocker: true, color: color); // HardLight: Capitalized OOC for consistency with other OOC chats.
break;
//Nyano - Summary: case adds the telepathic chat sending ability.
case InGameICChatType.Telepathic:
Expand Down
16 changes: 16 additions & 0 deletions Content.Server/_Crescent/ShipShields/ShipShieldsSystem.Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public void InitializeCommands()
[AdminCommand(AdminFlags.Debug)]
public void ShieldEntityCmd(IConsoleShell shell, string argstr, string[] args)
{
// HardLight start
if (args.Length < 1)
{
shell.WriteError("Usage: shieldentity <uid>");
return;
}
// HardLight end

if (!EntityUid.TryParse(args[0], out var uid))
{
shell.WriteError("Couldn't parse entity.");
Expand All @@ -34,6 +42,14 @@ public void ShieldEntityCmd(IConsoleShell shell, string argstr, string[] args)
[AdminCommand(AdminFlags.Debug)]
public void UnshieldEntityCmd(IConsoleShell shell, string argstr, string[] args)
{
// HardLight start
if (args.Length < 1)
{
shell.WriteError("Usage: unshieldentity <uid>");
return;
}
// HardLight end

if (!EntityUid.TryParse(args[0], out var uid))
{
shell.WriteError("Couldn't parse entity.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ private void OnRemoved(Entity<ShipShieldEmitterComponent> owner,ref ComponentRem
{
var parent = Transform(owner.Owner).GridUid;
if (parent is null)
{
RemoveEmitterShield(owner.Owner, owner.Comp); // HardLight
return;
UnshieldEntity(parent.Value, null);
}

RemoveEmitterShield(owner.Owner, owner.Comp, parent.Value); // HardLight
}

private void OnShieldDeflected(EntityUid uid, ShipShieldEmitterComponent component, ShieldDeflectedEvent args)
Expand Down
107 changes: 93 additions & 14 deletions Content.Server/_Crescent/ShipShields/ShipShieldsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,19 @@ public override void Update(float frameTime)
var parent = Transform(uid).GridUid;

if (parent == null)
return;
continue; // HardLight: return<continue

var filter = _station.GetInOwningStation(uid);

if (emitter.Damage > emitter.DamageLimit)
emitter.OverloadAccumulator = emitter.DamageOverloadTimePunishment;

// HardLight: Keep emitter shield reference in sync if shield was deleted externally.
if (emitter.Shield != null && !Exists(emitter.Shield.Value))
emitter.Shield = null;

// Check if we need to create a shield (not recharging, no valid shield, not overloaded)
if (!emitter.Recharging && !Exists(emitter.Shield) && emitter.OverloadAccumulator < 1)
if (!emitter.Recharging && !HasEmitterShield(uid, parent.Value, emitter) && emitter.OverloadAccumulator < 1) // HardLight: Exists(emitter.Shield)<HasEmitterShield(uid, parent.Value, emitter)
{
var shield = ShieldEntity(parent.Value, source: uid);
if (shield != EntityUid.Invalid)
Expand All @@ -98,13 +102,13 @@ public override void Update(float frameTime)
}
}
// Check if we need to remove shield (recharging or overloaded, and shield exists)
else if ((emitter.Recharging || emitter.OverloadAccumulator > 0) && Exists(emitter.Shield))
// HardLight start
else if (emitter.Recharging || emitter.OverloadAccumulator > 0)
{
UnshieldEntity(parent.Value);
emitter.Shield = null;
emitter.Shielded = null;
_audio.PlayGlobal(emitter.PowerDownSound, filter, true, emitter.PowerUpSound.Params);
if (RemoveEmitterShield(uid, emitter, parent.Value))
_audio.PlayGlobal(emitter.PowerDownSound, filter, true, emitter.PowerDownSound.Params);
}
// HardLight end

}
}
Expand All @@ -127,7 +131,7 @@ private void OnShieldedMapInit(EntityUid uid, ShipShieldedComponent component, M
{
QueueDel(component.Shield);
}

// Remove the component so the emitter can recreate it fresh
RemCompDeferred<ShipShieldedComponent>(uid);
}
Expand Down Expand Up @@ -191,14 +195,16 @@ private void OnCollide(EntityUid uid, ShipShieldComponent component, StartCollid

private void OnEmitterShutdown(EntityUid uid, ShipShieldEmitterComponent emitter, ComponentShutdown args) // Mono
{
// Only try to unshield if the shielded entity still exists and isn't being deleted
// (prevents double-deletion when the grid is being recursively deleted)
if (emitter.Shielded != null && Exists(emitter.Shielded.Value) && !Terminating(emitter.Shielded.Value))
// HardLight start
var parent = Transform(uid).GridUid;
if (parent == null || !Exists(parent.Value) || Terminating(parent.Value))
{
UnshieldEntity(emitter.Shielded.Value);
emitter.Shield = null;
emitter.Shielded = null;
RemoveEmitterShield(uid, emitter);
return;
}

RemoveEmitterShield(uid, emitter, parent.Value);
// HardLight end
}

/// <summary>
Expand Down Expand Up @@ -280,6 +286,79 @@ private bool UnshieldEntity(EntityUid uid, ShipShieldedComponent? component = nu
return true;
}

// HardLight start
private bool HasEmitterShield(EntityUid emitterUid, EntityUid gridUid, ShipShieldEmitterComponent emitter)
{
if (emitter.Shield != null && Exists(emitter.Shield.Value))
return true;

if (TryComp<ShipShieldedComponent>(gridUid, out var shielded)
&& shielded.Source == emitterUid
&& Exists(shielded.Shield))
{
emitter.Shield = shielded.Shield;
emitter.Shielded = gridUid;
return true;
}

return false;
}

private bool RemoveEmitterShield(EntityUid emitterUid, ShipShieldEmitterComponent emitter, EntityUid? gridUid = null)
{
var removed = false;

if (emitter.Shield != null && Exists(emitter.Shield.Value))
{
QueueDel(emitter.Shield.Value);
removed = true;
}

if (gridUid != null && TryComp<ShipShieldedComponent>(gridUid.Value, out var shielded) && shielded.Source == emitterUid)
{
if (Exists(shielded.Shield))
{
QueueDel(shielded.Shield);
removed = true;
}

RemComp<ShipShieldedComponent>(gridUid.Value);
}
else
{
// Fallback: cleanup any stale shielded markers that still point to this emitter.
var shieldedQuery = EntityQueryEnumerator<ShipShieldedComponent>();
while (shieldedQuery.MoveNext(out var shieldedUid, out var shieldedComp))
{
if (shieldedComp.Source != emitterUid)
continue;

if (Exists(shieldedComp.Shield))
{
QueueDel(shieldedComp.Shield);
removed = true;
}

RemComp<ShipShieldedComponent>(shieldedUid);
}
}

var shieldQuery = EntityQueryEnumerator<ShipShieldComponent>();
while (shieldQuery.MoveNext(out var shieldUid, out var shieldComp))
{
if (shieldComp.Source != emitterUid)
continue;

QueueDel(shieldUid);
removed = true;
}

emitter.Shield = null;
emitter.Shielded = null;
return removed;
}
// HardLight end

private ChainShape GenerateOvalFixture(EntityUid uid, string name, PhysicsComponent physics, MapGridComponent mapGrid, float padding = Padding)
{
float radius;
Expand Down
3 changes: 3 additions & 0 deletions Content.Server/_NF/Shipyard/Systems/ShipyardGridSaveSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,9 @@ private void SanitizeShipSaveNode(MappingDataNode root)
"VendingMachineTankDispenserEVAPOI",
"VendingMachineVendomatPOI",
"VendingMachineYouToolPOI",
// Everything else
"ReactorGasPipe", // Nuclear reactors duplicate invisible inlet/outlet pipes on save.
"ShipShield", // Ship shield emitters duplicate ship shield visuals on save.
"BaseMercenaryUplinkRadio",
"BaseUplinkRadio",
"BaseUplinkRadio20TC",
Expand Down
43 changes: 39 additions & 4 deletions Content.Server/_NF/Shipyard/Systems/ShipyardSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ public bool TryPurchaseShuttle(EntityUid consoleUid, ResPath shuttlePath, [NotNu
}

_shuttle.TryFTLDock(grid, shuttleComponent, targetGrid);
QueueShipyardMapCleanupIfEmpty(); // HardLight
shuttleEntityUid = grid;
return true;
}
Expand Down Expand Up @@ -261,6 +262,7 @@ public bool TryPurchaseShuttleFromFile(EntityUid consoleUid, ResPath shuttlePath
}

_shuttle.TryFTLDock(grid, shuttleComponent, targetGrid);
QueueShipyardMapCleanupIfEmpty(); // HardLight
shuttleEntityUid = grid;
return true;
}
Expand Down Expand Up @@ -319,7 +321,7 @@ private bool TryPurchaseShuttleFromYamlData(EntityUid consoleUid, string yamlDat
}
catch (Exception ex)
{
//_sawmill.Error($"Failed to purchase shuttle from YAML data: {ex.Message}");
_sawmill.Warning($"Failed to purchase shuttle from YAML data: {ex.Message}"); // HardLight: Error<Warning
return false;
}
finally
Expand Down Expand Up @@ -450,8 +452,7 @@ public ShipyardSaleResult TrySellShuttle(EntityUid shuttleUid, EntityUid console
if (!HasComp<ShuttleComponent>(shuttleUid)
|| !_transformQuery.TryComp(shuttleUid, out var xform)
|| !_transformQuery.TryComp(consoleUid, out var consoleXform)
|| consoleXform.GridUid == null
|| ShipyardMap == null)
|| consoleXform.GridUid == null) // HardLight
{
result.Error = ShipyardSaleError.InvalidShip;
return result;
Expand Down Expand Up @@ -559,23 +560,57 @@ private void CleanupShipyard()
if (ShipyardMap == null || !_map.MapExists(ShipyardMap.Value))
{
ShipyardMap = null;
_shuttleIndex = 0f; // HardLight
return;
}

_map.DeleteMap(ShipyardMap.Value);
ShipyardMap = null; // HardLight
_shuttleIndex = 0f; // HardLight
}

public void SetupShipyardIfNeeded()
{
if (ShipyardMap != null && _map.MapExists(ShipyardMap.Value))
return;

_map.CreateMap(out var shipyardMap);
var shipyardMapUid = _map.CreateMap(out var shipyardMap); // HardLight
ShipyardMap = shipyardMap;
_metaData.SetEntityName(shipyardMapUid, "Shipyard"); // HardLight

_map.SetPaused(ShipyardMap.Value, false);
}

/// <summary>
/// HardLight: Cleans up the shipyard staging map once no grids remain on it.
/// This avoids orphaned blank MapEntity entries after loading ships.
/// </summary>
private void QueueShipyardMapCleanupIfEmpty(int attempt = 0)
{
if (ShipyardMap == null || !_map.MapExists(ShipyardMap.Value))
{
ShipyardMap = null;
_shuttleIndex = 0f;
return;
}

var shipyardMap = ShipyardMap.Value;
var gridQuery = EntityManager.AllEntityQueryEnumerator<MapGridComponent, TransformComponent>();
while (gridQuery.MoveNext(out _, out _, out var xform))
{
if (xform.MapID == shipyardMap)
{
if (attempt >= 24)
return;

Timer.Spawn(TimeSpan.FromSeconds(5), () => QueueShipyardMapCleanupIfEmpty(attempt + 1));
return;
}
}

CleanupShipyard();
}

// <summary>
// Tries to rename a shuttle deed and update the respective components.
// Returns true if successful.
Expand Down
31 changes: 30 additions & 1 deletion Content.Shared/Salvage/Fulton/SharedFultonSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,36 @@ public override void Initialize()
SubscribeLocalEvent<FultonedComponent, ExaminedEvent>(OnFultonedExamine);
SubscribeLocalEvent<FultonedComponent, EntGotInsertedIntoContainerMessage>(OnFultonContainerInserted);

SubscribeLocalEvent<FultonComponent, MapInitEvent>(OnFultonMapInit);
SubscribeLocalEvent<FultonComponent, AfterInteractEvent>(OnFultonInteract);
SubscribeLocalEvent<FultonBeaconComponent, ComponentShutdown>(OnBeaconShutdown);

SubscribeLocalEvent<FultonComponent, StackSplitEvent>(OnFultonSplit);
}

private void OnFultonMapInit(Entity<FultonComponent> ent, ref MapInitEvent args)
{
if (ent.Comp.Beacon == null || Exists(ent.Comp.Beacon.Value))
return;

ent.Comp.Beacon = null;
Dirty(ent);
}

private void OnBeaconShutdown(EntityUid uid, FultonBeaconComponent component, ComponentShutdown args)
{
var query = EntityQueryEnumerator<FultonComponent>();

while (query.MoveNext(out var fultonUid, out var fulton))
{
if (fulton.Beacon != uid)
continue;

fulton.Beacon = null;
Dirty(fultonUid, fulton);
}
}

private void OnFultonContainerInserted(EntityUid uid, FultonedComponent component, EntGotInsertedIntoContainerMessage args)
{
RemCompDeferred<FultonedComponent>(uid);
Expand Down Expand Up @@ -116,12 +141,14 @@ private void OnFultonInteract(EntityUid uid, FultonComponent component, AfterInt
if (!_foldable.IsFolded(args.Target.Value))
{
component.Beacon = args.Target.Value;
Dirty(uid, component);
Audio.PlayPredicted(beacon.LinkSound, uid, args.User);
_popup.PopupClient(Loc.GetString("fulton-linked"), uid, args.User);
}
else
{
component.Beacon = EntityUid.Invalid;
component.Beacon = null;
Dirty(uid, component);
_popup.PopupClient(Loc.GetString("fulton-folded"), uid, args.User);
}

Expand All @@ -130,6 +157,8 @@ private void OnFultonInteract(EntityUid uid, FultonComponent component, AfterInt

if (Deleted(component.Beacon))
{
component.Beacon = null;
Dirty(uid, component);
_popup.PopupClient(Loc.GetString("fulton-not-found"), uid, args.User);
return;
}
Expand Down
4 changes: 3 additions & 1 deletion Content.Shared/SubFloor/SharedSubFloorHideSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ public enum SubFloorVisuals : byte
[Serializable, NetSerializable]
public enum SubfloorLayers : byte
{
FirstLayer
FirstLayer,
// HL - Add another layer, because... reasons (Pucklight commit)
SecondLayer
}
}
// Re-enable warnings if they were disabled elsewhere
Expand Down
Loading
Loading