Run Re-Do

This commit is contained in:
2026-07-02 20:41:43 -07:00
parent 86575dd5bc
commit 16e396841e
188 changed files with 8291 additions and 2429 deletions
@@ -38,13 +38,14 @@ namespace ProjectM.Client
UnityEngine.Camera _camera; // cursor -> ground re-raycast for click-to-place (resolved lazily)
GameObject _ghost; // translucent ground preview cube
Material _ghostMat;
MeshFilter _ghostMf; // ghost mesh swaps to the selected structure's preview mesh (HudTheme)
MeshRenderer _ghostMr;
Mesh _cubeMesh; // procedural fallback (the original preview cube)
byte _ghostType = 255; // last-applied preview type (255 = unset)
byte _lastSelected; // skip placing on the frame a palette click changes the selection
// DR-042 C6a: the ability-upgrade send is RUNTIME (the HUD Aether button calls UpgradeAbility); only the
// execute_code PLACE statics stay editor-gated. Mirrors EquipSendSystem's unconditional queue + drain.
static int s_PendingUpgrades = 0;
/// <summary>Runtime hook (HUD Aether button) + execute_code: queue an ability-damage upgrade.</summary>
public static void UpgradeAbility() => s_PendingUpgrades++;
// (Step 11: UpgradeAbility/s_PendingUpgrades RETIRED with the AbilityUpgradeRequest wire — in-run boons +
// the base meta-shop replaced the Aether damage upgrade.)des++;
#if UNITY_EDITOR
struct PendingBuild { public byte Type; public int CellX; public int CellZ; public byte Direction; }
@@ -113,17 +114,8 @@ namespace ProjectM.Client
foreach (var (key, type) in s_BuildHotkeys)
if (keyboard[key].wasPressedThisFrame && TryGetLocalPlayerCell(out int2 cell))
SendBuild(connection, type, cell.x, cell.y, type == StructureType.Conveyor ? s_ConveyorDir : (byte)0);
if (keyboard.uKey.wasPressedThisFrame)
SendUpgrade(connection);
}
// DR-042 C6a: the ability-upgrade drain runs at RUNTIME (the HUD Aether button enqueues via UpgradeAbility);
// only the execute_code PLACE drain stays editor-gated.
while (s_PendingUpgrades > 0)
{
s_PendingUpgrades--;
SendUpgrade(connection);
}
#if UNITY_EDITOR
while (s_PendingBuild.Count > 0)
{
@@ -185,7 +177,7 @@ namespace ProjectM.Client
byte reason = BuildPreviewMath.Evaluate(anchor, targetCell, occupied, LedgerOre(), cost);
bool valid = reason == BuildPreviewMath.Valid;
ShowGhost(BaseGridMath.CellToWorld(anchor, targetCell), anchor.CellSize, valid);
ShowGhost(BaseGridMath.CellToWorld(anchor, targetCell), anchor.CellSize, valid, sel);;
// Place on a left-click (valid, not the selecting click).
if (valid && !justSelected && mouse.leftButton.wasPressedThisFrame)
@@ -208,16 +200,44 @@ namespace ProjectM.Client
return int.MaxValue;
}
// ---- Ground ghost preview (procedural translucent cube, like AimReticleSystem's reticle) ----
void ShowGhost(float3 center, float cellSize, bool valid)
// ---- Ground ghost preview: the selected structure's REAL mesh (HudTheme, build-safe serialized refs)
// tinted translucent green/red; falls back to the original procedural cube when no mesh is authored. ----
void ShowGhost(float3 center, float cellSize, bool valid, byte type)
{
EnsureGhost();
_ghost.transform.position = (Vector3)center + Vector3.up * 0.5f;
_ghost.transform.localScale = new Vector3(cellSize * 0.9f, 1f, cellSize * 0.9f);
ApplyGhostMesh(type);
if (_ghostMf.sharedMesh == _cubeMesh)
{
_ghost.transform.position = (Vector3)center + Vector3.up * 0.5f;
_ghost.transform.localScale = new Vector3(cellSize * 0.9f, 1f, cellSize * 0.9f);
}
else
{
// preview meshes are authored real-size with a ground pivot (SM_Turret_01 / SM_Wall_01 / SM_Fabricator_01)
_ghost.transform.position = (Vector3)center;
_ghost.transform.localScale = Vector3.one;
}
_ghostMat.color = valid ? new Color(0.3f, 1f, 0.45f, 0.35f) : new Color(1f, 0.32f, 0.26f, 0.35f);
if (!_ghost.activeSelf) _ghost.SetActive(true);
}
// Swap the ghost's mesh when the palette selection changes (255 = unset sentinel; no selection is 0 -> cube).
void ApplyGhostMesh(byte type)
{
if (type == _ghostType) return;
_ghostType = type;
var theme = HudTheme.Get();
Mesh mesh = theme != null ? theme.StructureGhostMesh(type) : null;
if (mesh == null) mesh = _cubeMesh;
_ghostMf.sharedMesh = mesh;
if (_ghostMr.sharedMaterials.Length != mesh.subMeshCount)
{
var mats = new Material[mesh.subMeshCount]; // one translucent mat per submesh so the whole preview tints
for (int i = 0; i < mats.Length; i++) mats[i] = _ghostMat;
_ghostMr.sharedMaterials = mats;
}
}
void HideGhost()
{
if (_ghost != null && _ghost.activeSelf) _ghost.SetActive(false);
@@ -233,10 +253,13 @@ namespace ProjectM.Client
_ghost.name = "~BuildGhost";
var col = _ghost.GetComponent<Collider>();
if (col != null) Object.Destroy(col);
var mr = _ghost.GetComponent<MeshRenderer>();
mr.sharedMaterial = _ghostMat;
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
mr.receiveShadows = false;
_ghostMf = _ghost.GetComponent<MeshFilter>();
_cubeMesh = _ghostMf.sharedMesh;
_ghostType = 255;
_ghostMr = _ghost.GetComponent<MeshRenderer>();
_ghostMr.sharedMaterial = _ghostMat;
_ghostMr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
_ghostMr.receiveShadows = false;
_ghost.SetActive(false);
}
@@ -270,12 +293,6 @@ namespace ProjectM.Client
EntityManager.AddComponentData(e, new BuildPlaceRequest { StructureType = type, CellX = cellX, CellZ = cellZ, Direction = direction });
EntityManager.AddComponentData(e, new SendRpcCommandRequest { TargetConnection = connection });
}
void SendUpgrade(Entity connection)
{
var e = EntityManager.CreateEntity();
EntityManager.AddComponentData(e, new AbilityUpgradeRequest());
EntityManager.AddComponentData(e, new SendRpcCommandRequest { TargetConnection = connection });
}
// (Step 11: the Aether ability-upgrade sender was RETIRED with AbilityUpgradeRequest — boons replaced it.)
}
}