Represents one spikeable wall face (either an arena edge or an outward-facing side of an obstacle). Stores geometry, ownership, spike lifecycle state, rendering sprites for the band and teeth, and a Play method that toggles/animates the teeth overlay.
using System.Collections.Generic;
using Sandbox;
namespace BlockParty;
/// <summary>Transition state of a wall face's phase-2 spikes.</summary>
public enum WallTrans { None, Adding, Retracting }
/// <summary>
/// One spikeable wall face — an arena edge OR one outward-facing side of an interior
/// <see cref="Obstacle"/>. Unifies the phase-2 spike hazard (grown by <see cref="BlockSpikey"/>)
/// across both: the 4 arena edges plus each obstacle's sides that border playable space.
///
/// An arena face renders as a single baked strip sprite; an obstacle face as a row of baked 5px
/// tooth tiles (the strip repeats every 5px), so a face of any length reads as one continuous
/// spike strip. Animation commands (<c>plain</c>/<c>add</c>/<c>spiked</c>/<c>retract</c>/
/// <c>spiked_alt</c>) are broadcast to every sprite via <see cref="Play"/>.
/// </summary>
public sealed class WallFace
{
/// <summary>Direction the spikes grow (into playable space) — the face's outward normal.</summary>
public Direction Normal;
/// <summary>Owning obstacle, or null for an arena edge.</summary>
public Obstacle Owner;
/// <summary>Arena faces carry the wall SIDE the legacy <c>HasSpikes(Direction)</c> API keys on
/// (Left/Right/Up/Down); <see cref="Direction.None"/> for obstacle faces.</summary>
public Direction ArenaSide;
/// <summary>The face line (spike-send particle target / geometry).</summary>
public Line Segment;
// Spike lifecycle (mirrors the original per-Direction dict state).
public bool SpikesPresent; // spikes exist on this face (grown, or mid grow-in/retract) — deadly only when !Switching
public bool Switching; // growing/retracting — harmless
public float Timer; // remaining live time
public WallTrans Trans;
public float TransTimer;
/// <summary>Level-authored permanent spikes: deadly from the first frame and never retract (no
/// timer countdown, no warning blink, unaffected by <see cref="BlockSpikey"/> grafts since it's
/// already live). Set at build time from <see cref="LevelDef.SpikedWalls"/> /
/// <see cref="LevelDef.SpikedObstacleSides"/>.</summary>
public bool Permanent;
/// <summary>The plain wall BAND sprite: one exact-size quad for a tiled face or the baked arena strip.
/// Drawn ONCE at build time on <see cref="ORDER_BAND"/> with the <see cref="BandColor"/> tint
/// and never re-animated — so a spiked segment's band matches the adjacent
/// non-spiked walls. The spike hazard is drawn separately by <see cref="TeethSprites"/>.</summary>
public readonly List<SpriteRenderer> Sprites = new();
/// <summary>The tiled spike TEETH overlay sprites on <see cref="ORDER_TEETH"/>.
/// Always white/untinted (a hazard reads the same across every level theme) and hidden while the
/// face shows only its plain band. These use the baked <c>*_teeth</c> sprites, whose art has the
/// wall band removed so only the teeth composite over the coloured band beneath.</summary>
public readonly List<SpriteRenderer> TeethSprites = new();
/// <summary>Build-time record of the band tint: the level's direct wall colour on a tiled face's
/// white quad (BuildTiledFace), or the multiplier mapping the baked grey strip to that colour on
/// an arena wall (AddWall). Never read back at runtime. The spike TEETH ignore it and always draw
/// white so a hazard reads the same across every theme.</summary>
public Color BandColor = Color.White;
/// <summary>SpriteLayer childOrder of the plain band. Band + elbow sprites are CREATED on this
/// sub-layer (see GameStage's AddWall / BuildTiledFace / AddElbowPatch).</summary>
public const int ORDER_BAND = 1;
/// <summary>SpriteLayer childOrder of the teeth overlay: one sub-layer nearer than
/// <see cref="ORDER_BAND"/> so the (band-less) teeth composite over the coloured band beneath.</summary>
public const int ORDER_TEETH = 2;
public void Play( string anim )
{
// The band (Sprites) is static — set once at build from the level's direct WallColor — so a
// spiked wall's band always matches the neighbouring non-spiked walls. Only the teeth overlay
// animates: white/untinted, and hidden when the face shows only its plain band.
bool teeth = anim != "plain";
foreach ( var s in TeethSprites )
{
s.Enabled = teeth;
if ( teeth )
s.PlayAnimation( anim );
}
}
}