Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Content.Server/Backmen/NPC/HTN/LaydownPrecondition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Server.NPC;
using Content.Server.NPC.HTN.Preconditions;
using Content.Shared.Standing;

namespace Content.Server.Backmen.NPC.HTN;

/// <summary>
/// Checks if the owner is laydown or not
/// </summary>
public sealed partial class LaydownPrecondition : HTNPrecondition
{
private StandingStateSystem _stand = default!;

[ViewVariables(VVAccess.ReadWrite)]
[DataField("isDown")]
public bool IsDown = true;

public override void Initialize(IEntitySystemManager sysManager)
{
base.Initialize(sysManager);
_stand = sysManager.GetEntitySystem<StandingStateSystem>();
}

public override bool IsMet(NPCBlackboard blackboard)
{
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);

return IsDown && _stand.IsDown(owner) ||
!IsDown && !_stand.IsDown(owner);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Server.NPC;
using Content.Server.NPC.HTN;
using Content.Server.NPC.HTN.PrimitiveTasks;
using Content.Shared.Standing;

namespace Content.Server.Backmen.NPC.HTN.PrimitiveTasks.Operators.Combat;

public sealed partial class StandOperator : HTNOperator
{
private StandingStateSystem _stand = default!;

[DataField("shutdownState")]
public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.TaskFinished;

public override void Startup(NPCBlackboard blackboard)
{
base.Startup(blackboard);
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
_stand.Stand(owner);
}

public override void Initialize(IEntitySystemManager sysManager)
{
base.Initialize(sysManager);
_stand = sysManager.GetEntitySystem<StandingStateSystem>();
}

public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
{
return HTNOperatorStatus.Finished;
}
}
9 changes: 8 additions & 1 deletion Content.Server/Backmen/Shipwrecked/NPCZombieSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
using Content.Server.Humanoid.Systems;
using Content.Server.RandomMetadata;
using Content.Server.Zombies;
using Content.Shared.Backmen.Language.Components;
using Content.Shared.Backmen.Surgery.Consciousness.Components;
using Content.Shared.Damage;
using Content.Shared.Standing;
using Content.Shared.Trigger;
using Content.Shared.Weapons.Melee;
using Content.Shared.Zombies;
Expand All @@ -22,6 +25,7 @@ public sealed class NpcZombieMakeEvent : EntityEventArgs
public sealed class NPCZombieSystem : EntitySystem
{
[Dependency] private readonly ZombieSystem _zombieSystem = default!;
[Dependency] private readonly StandingStateSystem _stateSystem = default!;

public override void Initialize()
{
Expand All @@ -38,9 +42,12 @@ private void OnZombifyEntity(NpcZombieMakeEvent ev)
if (TerminatingOrDeleted(ev.Target))
return;

_stateSystem.Stand(ev.Target);
_zombieSystem.ZombifyEntity(ev.Target);
EnsureComp<UniversalLanguageSpeakerComponent>(ev.Target);
RemComp<GhostTakeoverAvailableComponent>(ev.Target);
RemComp<GhostRoleComponent>(ev.Target);
RemComp<ConsciousnessComponent>(ev.Target);

var z = EnsureComp<ZombieComponent>(ev.Target);
z.BaseZombieInfectionChance = 0.0001f;
Expand Down Expand Up @@ -110,7 +117,7 @@ private void OnSpawnZombifiedStartup(EntityUid uid, ZombifiedOnSpawnComponent co
});
}

private readonly EntProtoId ZombieSurpriseDetector = "ZombieSurpriseDetector";
private static readonly EntProtoId ZombieSurpriseDetector = "ZombieSurpriseDetector";
private void OnZombieSurpriseInit(EntityUid uid, ZombieSurpriseComponent component, MapInitEvent args)
{
if (TerminatingOrDeleted(uid))
Expand Down
1 change: 0 additions & 1 deletion Content.Server/Backmen/Vampiric/BloodSuckerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public sealed class BloodSuckerSystem : SharedBloodSuckerSystem
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly BkmVampireLevelingSystem _leveling = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly NPCRetaliationSystem _retaliationSystem = default!;
[Dependency] private readonly SharedCuffableSystem _cuffableSystem = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly IGameTiming _timing = default!;
Expand Down
13 changes: 8 additions & 5 deletions Content.Server/Weapons/DamageMarkerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
// Lavaland Change Start
[Dependency] private readonly PressureEfficiencyChangeSystem _pressure = default!;
[Dependency] private readonly BackStabSystem _backstab = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
Expand All @@ -39,13 +38,17 @@ private void OnApplyMarkerBonus(EntityUid uid, DamageMarkerComponent component,

if (boost.BackstabBoost != null
&& _backstab.TryBackstab(uid, args.User, Angle.FromDegrees(45d), playSound: false))
{
_damageable.TryChangeDamage((uid,damageable),
(boost.BackstabBoost + boost.Boost) * pressureMultiplier,
origin: args.User);
(boost.BackstabBoost + boost.Boost) * pressureMultiplier,
origin: args.User);
}
else
{
_damageable.TryChangeDamage((uid,damageable),
boost.Boost * pressureMultiplier,
origin: args.User);
boost.Boost * pressureMultiplier,
origin: args.User);
}
}
}
// Lavaland Change End
Expand Down
3 changes: 3 additions & 0 deletions Content.Server/Zombies/ZombieSystem.Transform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Content.Shared.Backmen.Surgery.Pain;
using Content.Shared.Backmen.Surgery.Pain.Systems;
using Content.Shared.Body.Components;
using Content.Shared.Body.Systems;
using Content.Shared.CombatMode;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Hands.Components;
Expand Down Expand Up @@ -73,6 +74,7 @@ public sealed partial class ZombieSystem
[Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly SharedBodySystem _bodySystem = default!; // backmen

private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
Expand Down Expand Up @@ -271,6 +273,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null)
PainType.TraumaticPain,
nerveSys.Value);
}
_bodySystem.ForceRestoreBody(target, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Step 1: Find the ForceRestoreBody method definition
echo "=== Searching for ForceRestoreBody method signature ==="
rg -n --type cs 'ForceRestoreBody' --max-count=20

echo ""
echo "=== Examining the ZombieSystem.Transform.cs file around line 276 ==="
# First, locate the file
find . -name "ZombieSystem.Transform.cs" -type f

Repository: Rxup/space-station-14

Length of output: 702


🏁 Script executed:

#!/bin/bash
# Step 2: Read the context around line 276 in ZombieSystem.Transform.cs
echo "=== Code context around line 276 ==="
sed -n '250,290p' ./Content.Server/Zombies/ZombieSystem.Transform.cs | cat -n

echo ""
echo "=== Full method signature of ForceRestoreBody ==="
sed -n '249,260p' ./Content.Shared/Body/Systems/SharedBodySystem.Body.cs | cat -n

echo ""
echo "=== Check broader context (lines 259-280) ==="
sed -n '259,280p' ./Content.Server/Zombies/ZombieSystem.Transform.cs | cat -n

Repository: Rxup/space-station-14

Length of output: 3522


Упростите вызов, используя значение параметра по умолчанию.

Параметр removeWounds в методе ForceRestoreBody имеет значение по умолчанию true. Вызов _bodySystem.ForceRestoreBody(target, true) на строке 276 можно упростить до _bodySystem.ForceRestoreBody(target) без явной передачи параметра, совпадающего с умолчанием.

Размещение вызова вне блока проверки нервной системы является намеренным — метод применяется ко всем зомбифицированным сущностям. Параметр removeWounds достаточно ясно описывает назначение.

🤖 Prompt for AI Agents
In `@Content.Server/Zombies/ZombieSystem.Transform.cs` at line 276, Вызов
_bodySystem.ForceRestoreBody(target, true) использует явный аргумент,
совпадающий со значением параметра по умолчанию; упростите вызов, убрав передачу
true и вызвав _bodySystem.ForceRestoreBody(target) вместо
_bodySystem.ForceRestoreBody(target, true). Найдите использование
ForceRestoreBody в файле (в контексте переменной _bodySystem и параметра
removeWounds) и удалите явный true, оставив только target.

// Backmen Edit end

_faction.ClearFactions(target, dirty: false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
using System;
using System.Linq;
using System.Numerics;
using Content.Server._Lavaland.Procedural.Components;
using Content.Shared._Lavaland.Procedural.Components;
using Content.Shared._Lavaland.Procedural.Prototypes;
using Content.Shared.Maps;
using Robust.Server.Physics;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Random;

namespace Content.Server._Lavaland.Procedural.Systems;

public sealed partial class LavalandPlanetSystem
{
[Dependency] private readonly GridFixtureSystem _gridFixture = default!;
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;

private bool LoadGridRuin(
LavalandGridRuinPrototype ruin,
Entity<LavalandMapComponent> lavaland,
Entity<LavalandPreloaderComponent> preloader,
Random random,
ref Dictionary<string, Box2> ruinsBoundsDict,
ref List<Box2> usedSpace,
ref List<Vector2> coords)
{
EntityUid? spawned = null;
if (coords.Count == 0)
return false;

var coord = random.Pick(coords);
var mapXform = Transform(preloader);
Box2 ruinBox; // This is ruin box, but moved to it's correct coords on the map

// Check if we already calculated that boundary before, and if we didn't then calculate it now
if (!ruinsBoundsDict.TryGetValue(ruin.ID, out var box))
{
if (!_mapLoader.TryLoadGrid(mapXform.MapID, ruin.Path, out var spawnedBoundedGrid))
{
Log.Error($"Failed to load ruin {ruin.ID} onto dummy map, on stage of loading! AAAAA!!");
return false;
}

// It's not useless!
spawned = spawnedBoundedGrid.Value.Owner;

if (!_fixtureQuery.TryGetComponent(spawned, out var manager))
{
Log.Error($"Failed to load ruin {ruin.ID} onto dummy map, it doesn't have fixture component! AAAAA!!");
Del(spawned);
return false;
}

// Actually calculate ruin bound
var transform = _physics.GetRelativePhysicsTransform(spawned.Value, preloader.Owner);
// holy shit
var bounds = (from fixture in manager.Fixtures.Values where fixture.Hard select fixture.Shape.ComputeAABB(transform, 0).Rounded(0)).ToList();
// Round this list of boxes up to
var calculatedBox = _random.Pick(bounds);
foreach (var bound in bounds)
Comment on lines +56 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "LavalandPlanetSystem.Grid.cs" 2>/dev/null | head -20

Repository: Rxup/space-station-14

Length of output: 186


🏁 Script executed:

cat -n "Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs" | sed -n '50,70p'

Repository: Rxup/space-station-14

Length of output: 1108


🏁 Script executed:

# Search for _random.Pick implementation
rg "_random\.Pick" -A 5 -B 2 --type cs

Repository: Rxup/space-station-14

Length of output: 50378


🏁 Script executed:

# Search for the Random class implementation to understand Pick behavior
rg "Pick\(" --type cs | grep -i "random\|\.pick" | head -20

Repository: Rxup/space-station-14

Length of output: 2719


🏁 Script executed:

# Search for existing empty collection guards in similar contexts
rg "\.Count\s*==\s*0" --type cs | head -20

Repository: Rxup/space-station-14

Length of output: 2349


🏁 Script executed:

# Search for Random.Pick implementation in the codebase
rg "public.*Pick\(" --type cs -A 10 | grep -A 10 "IRobustRandom\|IEnumerable"

Repository: Rxup/space-station-14

Length of output: 3951


🏁 Script executed:

# Check the Random extension methods that implement Pick
find . -name "*.cs" -type f -exec grep -l "SharedRandomExtensions\|RandomExtensions" {} \; 2>/dev/null | head -5

Repository: Rxup/space-station-14

Length of output: 327


🏁 Script executed:

# Look at the actual Random.Pick implementation
fd -t f "RandomExtensions.cs" | head -3

Repository: Rxup/space-station-14

Length of output: 120


🏁 Script executed:

cat -n "Content.Shared/Random/Helpers/SharedRandomExtensions.cs" | head -100

Repository: Rxup/space-station-14

Length of output: 3899


🏁 Script executed:

# Search for the generic Pick<T> overload that works with IList/IEnumerable
rg "public static.*Pick.*IList|public static.*Pick.*IEnumerable" --type cs -A 8 -B 2

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

cat -n "Content.Shared/Random/Helpers/SharedRandomExtensions.cs" | sed -n '100,200p'

Repository: Rxup/space-station-14

Length of output: 4047


🏁 Script executed:

# Search for Pick<T> with IList or similar collections
rg "Pick.*IList|Pick.*List\<" --type cs -B 2 -A 8

Repository: Rxup/space-station-14

Length of output: 177


🏁 Script executed:

cat -n "Content.Shared/Random/Helpers/SharedRandomExtensions.cs" | tail -100

Repository: Rxup/space-station-14

Length of output: 4055


🏁 Script executed:

# Search for Pick method overloads that work with lists/collections
rg "public static.*Pick" --type cs Content.Shared/Random/Helpers/SharedRandomExtensions.cs

Repository: Rxup/space-station-14

Length of output: 1143


🏁 Script executed:

# Look in Robust.Shared for the base Pick implementation
fd -t f "RandomExtensions.cs" 2>/dev/null | xargs grep -l "Pick" 2>/dev/null | head -5

Repository: Rxup/space-station-14

Length of output: 120


🏁 Script executed:

# Search for Pick implementations in the entire codebase that work with lists
rg "Pick.*IList\|Pick.*IEnumerable\|Pick.*List" --type cs -B 2 -A 5 | head -100

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Look for the actual generic Pick method used on bounds (which is List<Box2>)
# Search files that might have the base implementation
find . -type f -name "*.cs" | xargs grep -l "public static.*Pick.*where.*:" | head -10

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Check robustness library or look directly in the used interfaces
rg "IRobustRandom.*Pick" --type cs -A 10 | head -150

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Search in the code for similar patterns where Pick is called on a list
rg "_random\.Pick.*bounds\|_random\.Pick.*list\|_random\.Pick.*\(\)" --type cs -B 3 -A 3

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Look for ANY Pick usage with angle brackets (generic)
rg "\.Pick\<" --type cs | head -20

Repository: Rxup/space-station-14

Length of output: 145


🏁 Script executed:

# Try to understand the actual usage at the problem location
cat -n "Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs" | sed -n '50,75p'

Repository: Rxup/space-station-14

Length of output: 1368


🏁 Script executed:

# Search for IEnumerable/List Pick implementations across the repo
rg "Pick.*this.*IEnumerable|Pick.*this.*IList" --type cs -B 2 -A 5

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Search for any other Pick method that might handle List
rg "Pick" "Content.Shared/Random/Helpers/SharedRandomExtensions.cs" --type cs

Repository: Rxup/space-station-14

Length of output: 1351


🏁 Script executed:

# Check if there's a generic Pick override that handles any enumerable
python3 << 'EOF'
import subprocess
import re

# Run rg to get all function definitions in the file
result = subprocess.run(
    ["rg", "public static.*Pick", "Content.Shared/Random/Helpers/SharedRandomExtensions.cs", "-A", "1"],
    capture_output=True,
    text=True
)

print("All Pick method signatures:")
print(result.stdout)
EOF

Repository: Rxup/space-station-14

Length of output: 151


🏁 Script executed:

# Look at what _random is - check the type definition
rg "_random" "Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs" -B 5 | head -30

Repository: Rxup/space-station-14

Length of output: 486


🏁 Script executed:

# Search for IRobustRandom usage and see what methods are available
rg "IRobustRandom.*Pick" --type cs -B 5 -A 10 | head -80

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Let's check if there's a generic Pick method that works with List in the Robust.Shared library or elsewhere
# by looking at actual usages to understand the signature
rg "Pick\(.*List\|Pick\(.*bounds" --type cs -B 3 -A 3 | head -100

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Search for how Pick is typically used with lists across the codebase
rg "_random\.Pick\(" --type cs -A 1 | grep -E "\.ToList|\.Values|bounds|entities" | head -30

Repository: Rxup/space-station-14

Length of output: 3362


🏁 Script executed:

# Try to understand by looking at test files
fd "RandomExtensionsTests" --type f | head -3

Repository: Rxup/space-station-14

Length of output: 118


🏁 Script executed:

cat -n "Content.Tests/Shared/Utility/RandomExtensionsTests.cs"

Repository: Rxup/space-station-14

Length of output: 1568


Добавьте проверку пустого списка перед вызовом _random.Pick.

Если у руины нет фиксчур с флагом Hard, список bounds будет пустым и вызов _random.Pick(bounds) выбросит исключение. Добавьте проверку, залогируйте ошибку, удалите временную сущность и верните false.

🐛 Предложенное исправление
            var bounds = (from fixture in manager.Fixtures.Values where fixture.Hard select fixture.Shape.ComputeAABB(transform, 0).Rounded(0)).ToList();
+           if (bounds.Count == 0)
+           {
+               Log.Error($"Ruin {ruin.ID} has no hard fixtures to compute bounds");
+               Del(spawned.Value);
+               return false;
+           }
            // Round this list of boxes up to
            var calculatedBox = _random.Pick(bounds);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Actually calculate ruin bound
var transform = _physics.GetRelativePhysicsTransform(spawned.Value, preloader.Owner);
// holy shit
var bounds = (from fixture in manager.Fixtures.Values where fixture.Hard select fixture.Shape.ComputeAABB(transform, 0).Rounded(0)).ToList();
// Round this list of boxes up to
var calculatedBox = _random.Pick(bounds);
foreach (var bound in bounds)
// Actually calculate ruin bound
var transform = _physics.GetRelativePhysicsTransform(spawned.Value, preloader.Owner);
// holy shit
var bounds = (from fixture in manager.Fixtures.Values where fixture.Hard select fixture.Shape.ComputeAABB(transform, 0).Rounded(0)).ToList();
if (bounds.Count == 0)
{
Log.Error($"Ruin {ruin.ID} has no hard fixtures to compute bounds");
Del(spawned.Value);
return false;
}
// Round this list of boxes up to
var calculatedBox = _random.Pick(bounds);
foreach (var bound in bounds)
🤖 Prompt for AI Agents
In `@Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs`
around lines 56 - 62, The code currently builds a list `bounds` from
`manager.Fixtures.Values` (filtering `fixture.Hard`) and then calls
`_random.Pick(bounds)` which will throw if `bounds` is empty; add a check after
computing `bounds` to handle empty lists: log an error mentioning the ruin
preloader/spawn (`spawned` and `preloader.Owner` or other contextual
identifiers), delete the temporary entity (`spawned.Value` or the same temp
entity used) and return false from the enclosing method; ensure you reference
the same variables used here (`_physics.GetRelativePhysicsTransform`,
`manager.Fixtures`, `fixture.Hard`, `_random.Pick`) so the fix is applied in the
same block.

{
calculatedBox = calculatedBox.Union(bound);
}

// Safety measure
calculatedBox = calculatedBox.Enlarged(8f);

// Add calculated box to dictionary
ruinsBoundsDict.Add(ruin.ID, calculatedBox);

// Move our calculated box to correct position
var v1 = calculatedBox.BottomLeft + coord;
var v2 = calculatedBox.TopRight + coord;
ruinBox = new Box2(v1, v2);

// Teleport it into place on preloader map
_transform.SetCoordinates(spawned.Value, new EntityCoordinates(preloader, coord));
}
else
{
// Why there's no method to move the Box2 around???
var v1 = box.BottomLeft + coord;
var v2 = box.TopRight + coord;
ruinBox = new Box2(v1, v2);
}

// If any used boundary intersects with current boundary, return
if (usedSpace.Any(used => used.Intersects(ruinBox)))
{
Log.Debug("Ruin can't be placed on it's coordinates, skipping spawn");
return false;
}
Comment on lines +89 to +94
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs | head -120 | tail -40

Repository: Rxup/space-station-14

Length of output: 186


🏁 Script executed:

cat -n Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs | head -150 | tail -80

Repository: Rxup/space-station-14

Length of output: 4440


🏁 Script executed:

wc -l Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs

Repository: Rxup/space-station-14

Length of output: 141


🏁 Script executed:

sed -n '1,150p' Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs | cat -n

Repository: Rxup/space-station-14

Length of output: 7653


Удалите временный грид при отказе размещения.

При первом вычислении границ грид загружается и помещается на карту preloader (строка 79). Если проверка пересечения (строка 90) обнаружит конфликт, метод возвращает false, но загруженный грид остаётся на preloader-карте, так как не удаляется. Это приводит к накоплению неиспользованных сущностей.

🐛 Исправление
         if (usedSpace.Any(used => used.Intersects(ruinBox)))
         {
             Log.Debug("Ruin can't be placed on it's coordinates, skipping spawn");
+            if (spawned != null)
+                Del(spawned.Value);
             return false;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If any used boundary intersects with current boundary, return
if (usedSpace.Any(used => used.Intersects(ruinBox)))
{
Log.Debug("Ruin can't be placed on it's coordinates, skipping spawn");
return false;
}
// If any used boundary intersects with current boundary, return
if (usedSpace.Any(used => used.Intersects(ruinBox)))
{
Log.Debug("Ruin can't be placed on it's coordinates, skipping spawn");
if (spawned != null)
Del(spawned.Value);
return false;
}
🤖 Prompt for AI Agents
In `@Content.Server/_Lavaland/Procedural/Systems/LavalandPlanetSystem.Grid.cs`
around lines 89 - 94, When the intersection check (usedSpace.Any(used =>
used.Intersects(ruinBox))) fails you must remove the temporary grid that was
loaded onto the preloader before returning false; locate the variable that holds
the loaded grid (the temporary grid/entity placed on the preloader map when
computing bounds) and ensure you call the appropriate removal method (e.g.
_entityManager.DeleteEntity(loadedGrid) or preloader.RemoveEntity(loadedGrid))
right before the early return in the failing branch so the unused grid entity is
not left on the preloader.


// Try to load it on a dummy map if it wasn't already
if (spawned == null)
{
if (!_mapLoader.TryLoadGrid(mapXform.MapID, ruin.Path, out var spawnedGrid, offset: coord))
{
Log.Error($"Failed to load ruin {ruin.ID} onto dummy map, on stage of reparenting it to Lavaland! (this is really bad)");
return false;
}

spawned = spawnedGrid.Value.Owner;
}

// Set its position to Lavaland
var spawnedXForm = _xformQuery.GetComponent(spawned.Value);
_metaData.SetEntityName(spawned.Value, Loc.GetString(ruin.Name));
_transform.SetParent(spawned.Value, spawnedXForm, lavaland);
_transform.SetCoordinates(spawned.Value, new EntityCoordinates(lavaland, spawnedXForm.Coordinates.Position.Rounded()));

// Merge fixtures from lavaland grid to spawned ruin grid
if (HasComp<MapGridComponent>(lavaland.Owner))
{
var sourceGridUid = lavaland.Owner;

if (TryComp<MapGridComponent>(spawned.Value, out var spawnedGrid) &&
TryComp<MapGridComponent>(sourceGridUid, out var sourceGrid) &&
sourceGridUid != spawned.Value)
{
try
{
// Get the position of source grid (lavaland) in local coordinates of target grid (spawned)
var sourceWorldPos = _transform.GetWorldPosition(sourceGridUid);
var localPos = _map.WorldToLocal(spawned.Value, spawnedGrid, sourceWorldPos);
var offset = (Vector2i)localPos;

// Get the rotation of the target grid
var rotation = Transform(spawned.Value).LocalRotation;

// Replace empty tiles in spawned grid with tiles from the same position in lavaland grid
foreach (var tile in _map.GetAllTiles(spawned.Value, spawnedGrid, false))
{
if (tile.Tile == Tile.Empty)
{
// Get world position of this tile
var tileWorldPos = _map.GridTileToWorldPos(spawned.Value, spawnedGrid, tile.GridIndices);

// Convert to local coordinates in lavaland grid
var lavalandTileIndices = _map.WorldToTile(sourceGridUid, sourceGrid, tileWorldPos);

// Get tile from lavaland grid at this position
if (_map.TryGetTileRef(sourceGridUid, sourceGrid, lavalandTileIndices, out var lavalandTile) &&
!lavalandTile.Tile.IsEmpty)
{
_map.SetTile(spawned.Value, spawnedGrid, tile.GridIndices, lavalandTile.Tile);
}
}
}

_gridFixture.Merge(sourceGridUid, spawned.Value, offset, rotation);
}
catch (Exception ex)
{
Log.Error($"Failed to merge fixtures for ruin {ruin.ID}: {ex}");
}
}
}

// yaaaaaaaaaaaaaaaay
usedSpace.Add(ruinBox);
coords.Remove(coord);
return true;
}
}
Loading
Loading