Adding big batch of structural code provided by Claude to start designing levels
This commit is contained in:
parent
0ed4df8bc9
commit
a4e28bc93f
26 changed files with 1951 additions and 0 deletions
118
Assets/_Project/Levels/SpawnerVolume.cs
Normal file
118
Assets/_Project/Levels/SpawnerVolume.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
using UnityEngine;
|
||||
using TD.Core;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace TD.Levels
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring volume marking where enemies spawn into a player's zone. Spawner tiles are
|
||||
/// <see cref="PlacementState.Restricted"/> in the baked grid (no tower placement allowed)
|
||||
/// but remain walkable so enemies can leave the spawner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A zone may have multiple spawners; <see cref="spawnerIdInZone"/> disambiguates them.
|
||||
/// Player 5 in the 9-player Wintermaul map is the canonical multi-spawner zone.
|
||||
///
|
||||
/// The spawner declares an owning player via <see cref="owner"/> rather than relying on
|
||||
/// spatial containment within a PlayerZoneVolume. The bake validates (with a soft warning)
|
||||
/// that the spawner's tiles fall inside its declared owner's zone.
|
||||
/// </remarks>
|
||||
public class SpawnerVolume : VolumeBase
|
||||
{
|
||||
[Tooltip("Which player's zone owns this spawner. Explicit — not inferred from spatial overlap.")]
|
||||
public PlayerSlot owner = PlayerSlot.Player1;
|
||||
|
||||
[Tooltip("Disambiguator for zones with multiple spawners. Should be 0 for single-spawner zones, " +
|
||||
"and contiguous starting from 0 for multi-spawner zones (e.g., 0 and 1).")]
|
||||
public int spawnerIdInZone = 0;
|
||||
|
||||
[Tooltip("Direction enemies face when they spawn. Used for visual orientation and may bias " +
|
||||
"initial enemy movement direction.")]
|
||||
public Direction spawnFacing = Direction.South;
|
||||
|
||||
[Tooltip("Whether tiles in this volume are buildable. Defaults to Invalid (no placement on spawners).")]
|
||||
public PlacementValidity placementValidity = PlacementValidity.Invalid;
|
||||
|
||||
// Spawners draw above player zones so the player zone color reads as background.
|
||||
private const float FillYLevel = 0.06f;
|
||||
private const float ArrowYLevel = 0.07f;
|
||||
private const float ArrowLength = 1.5f; // 1.5 tiles per gizmo design
|
||||
|
||||
protected override bool GetAlwaysShowToggle(LevelAuthoring authoring)
|
||||
{
|
||||
return authoring.alwaysShowSpawners;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
DrawGizmosCore();
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (ShouldDrawAlwaysOn())
|
||||
{
|
||||
DrawGizmosCore();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGizmosCore()
|
||||
{
|
||||
Color baseColor = PlayerColors.Get(owner);
|
||||
DrawTileCoverageFill(baseColor, alpha: 0.40f, yLevel: FillYLevel);
|
||||
DrawRectangularOutline(baseColor, yLevel: FillYLevel);
|
||||
|
||||
// Direction arrow originates from the volume's center and extends 1.5 tiles in the
|
||||
// declared facing direction, rendered in opaque owner color.
|
||||
var col = Collider;
|
||||
if (col != null)
|
||||
{
|
||||
Vector3 arrowOrigin = new Vector3(col.bounds.center.x, ArrowYLevel, col.bounds.center.z);
|
||||
Vector3 arrowDir = DirectionToWorld(spawnFacing);
|
||||
DrawArrow(arrowOrigin, arrowDir, ArrowLength, baseColor);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Label conditional on multi-spawner status:
|
||||
// - Single-spawner zone: "Player N Spawn"
|
||||
// - Multi-spawner zone: "Player N Spawn 0", "Player N Spawn 1", etc.
|
||||
// Multi-spawner detection scans other SpawnerVolumes in the scene with the same owner.
|
||||
// Same posture as the leak-exit target lookup: cheap on realistic maps, can be cached
|
||||
// if it ever shows up as an editor-perf issue.
|
||||
if (col != null)
|
||||
{
|
||||
Vector3 labelPos = new Vector3(col.bounds.center.x, ArrowYLevel + 0.1f, col.bounds.center.z);
|
||||
string labelText = ZoneHasMultipleSpawners()
|
||||
? $"{FormatPlayerName(owner)} Spawn {spawnerIdInZone}"
|
||||
: $"{FormatPlayerName(owner)} Spawn";
|
||||
Handles.Label(labelPos, labelText);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if more than one active SpawnerVolume in the scene shares this spawner's
|
||||
/// <see cref="owner"/>. Used to decide whether to suffix the gizmo label with the spawner ID.
|
||||
/// </summary>
|
||||
private bool ZoneHasMultipleSpawners()
|
||||
{
|
||||
var spawners = Object.FindObjectsByType<SpawnerVolume>(FindObjectsInactive.Exclude);
|
||||
if (spawners == null) return false;
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < spawners.Length; i++)
|
||||
{
|
||||
if (spawners[i] == null) continue;
|
||||
if (spawners[i].owner == owner)
|
||||
{
|
||||
count++;
|
||||
if (count > 1) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue