Squashes 146 commits from battle-engine-extraction. Net: 2,045 files changed, +11,896 / -158,687 lines. Ships engine passes 4-7 (dead-code cull, view-layer stub, receive-path shrink) plus the Phase-5 AsyncLocal ambient deletion that turns concurrent battles into a type-system property rather than a scope contract. ## What landed **Passes 4-7 (chunks 1-34):** Extended the Phase-4 const-false collapse into a cascading cull across the skill graph, view layer, and receive-path periphery. Six mode flags (IsWatchBattle/IsReplayBattle/IsAdmin/IsAdminWatch/IsPuzzleQuest/ IsAINetwork) became `const false`, every guarded block deleted. Field*.cs subclass ctors + BackGroundBase + ObjectChecker culled to no-ops. Mulligan family reworked to take a mgr param through IMulliganMgr.InitMulligan. Emotion/Recovery/Resource clusters null-stubbed. Prediction/OperationSimulator/ skill filters converted from static ambient reads to per-mgr reads via SkillPrm.ownerCard.SelfBattlePlayer.BattleMgr / ins.BattleMgr / this.BattleMgr. **Phase-5 ambient rip (chunks 35-47):** Deleted BattleAmbient / BattleAmbient- Context / TestBattleScope in full. Every per-battle mutable slot now lives on the mgr instance itself: mgr.InstanceIsForecast / InstanceIsRandomDraw / InstanceRecoveryInfo / InstanceViewerId / InstanceNetworkAgent / GameMgr BattleManagerBase.GetIns() returns null unconditionally; the residual static flags + 3 façades (Certification.ViewerId, Data.BattleRecoveryInfo, ToolboxGame.RealTimeNetworkAgent) are null-tolerant defaults kept for the handful of engine-internal readers that still reference their types. Zero BattleAmbient references anywhere in engine + node + tests. Added pre-seeded GameMgr ctor overload threaded through the mgr chain (BattleManagerBase → SingleBattleMgr / NetworkBattleManagerBase → NetworkStandard- BattleMgr → HeadlessBattleMgr / HeadlessNetworkBattleMgr). Fixtures build a GameMgr, seed it via HeadlessEngineEnv.SeedCharaIds/SeedNetUser, and pass it to the mgr's ctor — no ambient reach. Node side (SVSim.BattleNode/SessionBattleEngine): _ctx replaced with a plain GameMgr field; 34 `using var _ambient = BattleAmbient.Enter(_ctx)` scope wraps ripped from every accessor and mutator; EngineGlobalInit.WirePerSessionGameMgr takes GameMgr as a param and runs from SessionBattleEngine.SetupInternal BEFORE mgr construction. Test side: TestBattleScope deleted; 18 fixture [SetUp]s migrated to `HeadlessEngineEnv.EnsureProcessGlobals()`; MultiInstanceEngineTests rewritten around per-mgr construction (GetIns() → null is the pinned invariant). ## Regression fixes - **chunk-48** (MulliganCtrl): chunk-35's `= null` stubs on card lookups broke the live receive-driven Deal path (BattlePlayerBase.DrawCard NRE'd downstream of NetworkPlayerMulliganCtrl.StartMulliganVfx). Restored the three lookups via `_battlePlayer.BattleMgr.GetBattleCardIdx`. Engine tests were satisfied by the WireMulliganPhase seam; unit tests exposed the live-path gap. ## Ship state - SVSim.BattleEngine.Tests: 56/56 pass, 2 skip - SVSim.UnitTests: 1554/1554 pass (was 1523/31-fail before chunk 48) - Solution build: 0 source warnings (40 pre-existing NU1902 MessagePack CVEs in SVSim.EmulatedEntrypoint, unrelated) - Sequential PVP smoke: verified live (two back-to-back battles, no regression on cleanup/spinup) - Concurrent PVP smoke: verified live Adds tools/engine-port/ClosureAnalyzer/ — the Roslyn transitive-type-closure analyzer needed to make future cascade cleanup safe (per feedback memory "Engine cleanup needs closure tool" from the 2026-06-28 pass-3 failure). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
552 lines
11 KiB
C#
552 lines
11 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
public abstract class UIRect : MonoBehaviour
|
|
{
|
|
[Serializable]
|
|
public class AnchorPoint
|
|
{
|
|
public Transform target;
|
|
|
|
public float relative;
|
|
|
|
public int absolute;
|
|
|
|
[NonSerialized]
|
|
public UIRect rect;
|
|
|
|
[NonSerialized]
|
|
public Camera targetCam;
|
|
|
|
public AnchorPoint()
|
|
{
|
|
}
|
|
|
|
public AnchorPoint(float relative)
|
|
{
|
|
this.relative = relative;
|
|
}
|
|
|
|
public void Set(float relative, float absolute)
|
|
{
|
|
this.relative = relative;
|
|
this.absolute = Mathf.FloorToInt(absolute + 0.5f);
|
|
}
|
|
|
|
public void SetHorizontal(Transform parent, float localPos)
|
|
{
|
|
if ((bool)rect)
|
|
{
|
|
Vector3[] sides = rect.GetSides(parent);
|
|
float num = Mathf.Lerp(sides[0].x, sides[2].x, relative);
|
|
absolute = Mathf.FloorToInt(localPos - num + 0.5f);
|
|
return;
|
|
}
|
|
Vector3 position = target.position;
|
|
if (parent != null)
|
|
{
|
|
position = parent.InverseTransformPoint(position);
|
|
}
|
|
absolute = Mathf.FloorToInt(localPos - position.x + 0.5f);
|
|
}
|
|
|
|
public void SetVertical(Transform parent, float localPos)
|
|
{
|
|
if ((bool)rect)
|
|
{
|
|
Vector3[] sides = rect.GetSides(parent);
|
|
float num = Mathf.Lerp(sides[3].y, sides[1].y, relative);
|
|
absolute = Mathf.FloorToInt(localPos - num + 0.5f);
|
|
return;
|
|
}
|
|
Vector3 position = target.position;
|
|
if (parent != null)
|
|
{
|
|
position = parent.InverseTransformPoint(position);
|
|
}
|
|
absolute = Mathf.FloorToInt(localPos - position.y + 0.5f);
|
|
}
|
|
|
|
public Vector3[] GetSides(Transform relativeTo)
|
|
{
|
|
if (target != null)
|
|
{
|
|
if (rect != null)
|
|
{
|
|
return rect.GetSides(relativeTo);
|
|
}
|
|
if (target.GetComponent<Camera>() != null)
|
|
{
|
|
return target.GetComponent<Camera>().GetSides(relativeTo);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public enum AnchorUpdate
|
|
{
|
|
OnEnable,
|
|
OnUpdate }
|
|
|
|
public AnchorPoint leftAnchor = new AnchorPoint();
|
|
|
|
public AnchorPoint rightAnchor = new AnchorPoint(1f);
|
|
|
|
public AnchorPoint bottomAnchor = new AnchorPoint();
|
|
|
|
public AnchorPoint topAnchor = new AnchorPoint(1f);
|
|
|
|
public AnchorUpdate updateAnchors = AnchorUpdate.OnUpdate;
|
|
|
|
[NonSerialized]
|
|
protected GameObject mGo;
|
|
|
|
[NonSerialized]
|
|
protected Transform mTrans;
|
|
|
|
[NonSerialized]
|
|
protected BetterList<UIRect> mChildren = new BetterList<UIRect>();
|
|
|
|
[NonSerialized]
|
|
protected bool mChanged = true;
|
|
|
|
[NonSerialized]
|
|
protected bool mParentFound;
|
|
|
|
[NonSerialized]
|
|
private bool mUpdateAnchors = true;
|
|
|
|
[NonSerialized]
|
|
private int mUpdateFrame = -1;
|
|
|
|
[NonSerialized]
|
|
private bool mAnchorsCached;
|
|
|
|
[NonSerialized]
|
|
private UIRoot mRoot;
|
|
|
|
[NonSerialized]
|
|
private UIRect mParent;
|
|
|
|
[NonSerialized]
|
|
private bool mRootSet;
|
|
|
|
[NonSerialized]
|
|
protected Camera mCam;
|
|
|
|
protected bool mStarted;
|
|
|
|
private bool _hasTrans;
|
|
|
|
[NonSerialized]
|
|
public float finalAlpha = 1f;
|
|
|
|
protected static Vector3[] mSides = new Vector3[4];
|
|
|
|
public GameObject cachedGameObject
|
|
{
|
|
get
|
|
{
|
|
if (mGo == null)
|
|
{
|
|
mGo = base.gameObject;
|
|
}
|
|
return mGo;
|
|
}
|
|
}
|
|
|
|
public Transform cachedTransform
|
|
{
|
|
get
|
|
{
|
|
if (!_hasTrans)
|
|
{
|
|
mTrans = base.transform;
|
|
_hasTrans = true;
|
|
}
|
|
return mTrans;
|
|
}
|
|
}
|
|
|
|
public Camera anchorCamera
|
|
{
|
|
get
|
|
{
|
|
if (!mAnchorsCached)
|
|
{
|
|
ResetAnchors();
|
|
}
|
|
return mCam;
|
|
}
|
|
}
|
|
|
|
public bool isFullyAnchored
|
|
{
|
|
get
|
|
{
|
|
if ((bool)leftAnchor.target && (bool)rightAnchor.target && (bool)topAnchor.target)
|
|
{
|
|
return bottomAnchor.target;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public virtual bool isAnchoredHorizontally
|
|
{
|
|
get
|
|
{
|
|
if (!leftAnchor.target)
|
|
{
|
|
return rightAnchor.target;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public virtual bool isAnchoredVertically
|
|
{
|
|
get
|
|
{
|
|
if (!bottomAnchor.target)
|
|
{
|
|
return topAnchor.target;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public virtual bool canBeAnchored => true;
|
|
|
|
public UIRect parent
|
|
{
|
|
get
|
|
{
|
|
if (!mParentFound)
|
|
{
|
|
mParentFound = true;
|
|
mParent = NGUITools.FindInParents<UIRect>(cachedTransform.parent);
|
|
}
|
|
return mParent;
|
|
}
|
|
}
|
|
|
|
public UIRoot root
|
|
{
|
|
get
|
|
{
|
|
if (parent != null)
|
|
{
|
|
return mParent.root;
|
|
}
|
|
if (!mRootSet)
|
|
{
|
|
mRootSet = true;
|
|
mRoot = NGUITools.FindInParents<UIRoot>(cachedTransform);
|
|
}
|
|
return mRoot;
|
|
}
|
|
}
|
|
|
|
public bool isAnchored
|
|
{
|
|
get
|
|
{
|
|
if ((bool)leftAnchor.target || (bool)rightAnchor.target || (bool)topAnchor.target || (bool)bottomAnchor.target)
|
|
{
|
|
return canBeAnchored;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public abstract float alpha { get; set; }
|
|
|
|
public abstract Vector3[] localCorners { get; }
|
|
|
|
public abstract Vector3[] worldCorners { get; }
|
|
|
|
protected float cameraRayDistance
|
|
{
|
|
get
|
|
{
|
|
if (anchorCamera == null)
|
|
{
|
|
return 0f;
|
|
}
|
|
if (!mCam.orthographic)
|
|
{
|
|
Transform transform = cachedTransform;
|
|
Transform transform2 = mCam.transform;
|
|
Plane plane = new Plane(transform.rotation * Vector3.back, transform.position);
|
|
Ray ray = new Ray(transform2.position, transform2.rotation * Vector3.forward);
|
|
if (plane.Raycast(ray, out var enter))
|
|
{
|
|
return enter;
|
|
}
|
|
}
|
|
return Mathf.Lerp(mCam.nearClipPlane, mCam.farClipPlane, 0.5f);
|
|
}
|
|
}
|
|
|
|
public void ReSearchUIRoot()
|
|
{
|
|
if (parent != null)
|
|
{
|
|
parent.ReSearchUIRoot();
|
|
}
|
|
else if (!mRootSet || !(mRoot != null))
|
|
{
|
|
mRoot = NGUITools.FindInParents<UIRoot>(cachedTransform);
|
|
if (mRoot != null)
|
|
{
|
|
mRootSet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract float CalculateFinalAlpha(int frameID);
|
|
|
|
public virtual void Invalidate(bool includeChildren)
|
|
{
|
|
mChanged = true;
|
|
if (includeChildren)
|
|
{
|
|
for (int i = 0; i < mChildren.size; i++)
|
|
{
|
|
mChildren.buffer[i].Invalidate(includeChildren: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual Vector3[] GetSides(Transform relativeTo)
|
|
{
|
|
if (anchorCamera != null)
|
|
{
|
|
return mCam.GetSides(cameraRayDistance, relativeTo);
|
|
}
|
|
Vector3 position = cachedTransform.position;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
mSides[i] = position;
|
|
}
|
|
if (relativeTo != null)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
mSides[j] = relativeTo.InverseTransformPoint(mSides[j]);
|
|
}
|
|
}
|
|
return mSides;
|
|
}
|
|
|
|
protected Vector3 GetLocalPos(AnchorPoint ac, Transform trans)
|
|
{
|
|
if (anchorCamera == null || ac.targetCam == null)
|
|
{
|
|
return cachedTransform.localPosition;
|
|
}
|
|
Rect rect = ac.targetCam.rect;
|
|
Vector3 vector = ac.targetCam.WorldToViewportPoint(ac.target.position);
|
|
Vector3 position = new Vector3(vector.x * rect.width + rect.x, vector.y * rect.height + rect.y, vector.z);
|
|
position = mCam.ViewportToWorldPoint(position);
|
|
if (trans != null)
|
|
{
|
|
position = trans.InverseTransformPoint(position);
|
|
}
|
|
position.x = Mathf.Floor(position.x + 0.5f);
|
|
position.y = Mathf.Floor(position.y + 0.5f);
|
|
return position;
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
mUpdateFrame = -1;
|
|
if (updateAnchors == AnchorUpdate.OnEnable)
|
|
{
|
|
mAnchorsCached = false;
|
|
mUpdateAnchors = true;
|
|
}
|
|
if (mStarted)
|
|
{
|
|
OnInit();
|
|
}
|
|
mUpdateFrame = -1;
|
|
}
|
|
|
|
protected virtual void OnInit()
|
|
{
|
|
mChanged = true;
|
|
mRootSet = false;
|
|
mParentFound = false;
|
|
if (parent != null)
|
|
{
|
|
mParent.mChildren.Add(this);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnDisable()
|
|
{
|
|
if ((bool)mParent)
|
|
{
|
|
mParent.mChildren.Remove(this);
|
|
}
|
|
mParent = null;
|
|
mRoot = null;
|
|
mRootSet = false;
|
|
mParentFound = false;
|
|
}
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
NGUITools.CheckForPrefabStage(base.gameObject);
|
|
mStarted = false;
|
|
mGo = base.gameObject;
|
|
mTrans = base.transform;
|
|
_hasTrans = true;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (!mAnchorsCached)
|
|
{
|
|
ResetAnchors();
|
|
}
|
|
int frameCount = Time.frameCount;
|
|
if (mUpdateFrame != frameCount)
|
|
{
|
|
if (updateAnchors == AnchorUpdate.OnUpdate || mUpdateAnchors)
|
|
{
|
|
UpdateAnchorsInternal(frameCount);
|
|
}
|
|
OnUpdate();
|
|
}
|
|
}
|
|
|
|
protected void UpdateAnchorsInternal(int frame)
|
|
{
|
|
mUpdateFrame = frame;
|
|
mUpdateAnchors = false;
|
|
bool flag = false;
|
|
if ((bool)leftAnchor.target)
|
|
{
|
|
flag = true;
|
|
if (leftAnchor.rect != null && leftAnchor.rect.mUpdateFrame != frame)
|
|
{
|
|
leftAnchor.rect.Update();
|
|
}
|
|
}
|
|
if ((bool)bottomAnchor.target)
|
|
{
|
|
flag = true;
|
|
if (bottomAnchor.rect != null && bottomAnchor.rect.mUpdateFrame != frame)
|
|
{
|
|
bottomAnchor.rect.Update();
|
|
}
|
|
}
|
|
if ((bool)rightAnchor.target)
|
|
{
|
|
flag = true;
|
|
if (rightAnchor.rect != null && rightAnchor.rect.mUpdateFrame != frame)
|
|
{
|
|
rightAnchor.rect.Update();
|
|
}
|
|
}
|
|
if ((bool)topAnchor.target)
|
|
{
|
|
flag = true;
|
|
if (topAnchor.rect != null && topAnchor.rect.mUpdateFrame != frame)
|
|
{
|
|
topAnchor.rect.Update();
|
|
}
|
|
}
|
|
if (flag)
|
|
{
|
|
OnAnchor();
|
|
}
|
|
}
|
|
|
|
public void UpdateAnchors()
|
|
{
|
|
if (isAnchored)
|
|
{
|
|
mUpdateFrame = -1;
|
|
mUpdateAnchors = true;
|
|
UpdateAnchorsInternal(Time.frameCount);
|
|
}
|
|
}
|
|
|
|
protected abstract void OnAnchor();
|
|
|
|
public void SetAnchor(Transform t)
|
|
{
|
|
leftAnchor.target = t;
|
|
rightAnchor.target = t;
|
|
topAnchor.target = t;
|
|
bottomAnchor.target = t;
|
|
ResetAnchors();
|
|
UpdateAnchors();
|
|
}
|
|
|
|
public void ResetAnchors()
|
|
{
|
|
mAnchorsCached = true;
|
|
leftAnchor.rect = (leftAnchor.target ? leftAnchor.target.GetComponent<UIRect>() : null);
|
|
bottomAnchor.rect = (bottomAnchor.target ? bottomAnchor.target.GetComponent<UIRect>() : null);
|
|
rightAnchor.rect = (rightAnchor.target ? rightAnchor.target.GetComponent<UIRect>() : null);
|
|
topAnchor.rect = (topAnchor.target ? topAnchor.target.GetComponent<UIRect>() : null);
|
|
mCam = NGUITools.FindCameraForLayer(cachedGameObject.layer);
|
|
FindCameraFor(leftAnchor);
|
|
FindCameraFor(bottomAnchor);
|
|
FindCameraFor(rightAnchor);
|
|
FindCameraFor(topAnchor);
|
|
mUpdateAnchors = true;
|
|
}
|
|
|
|
public void ResetAndUpdateAnchors()
|
|
{
|
|
ResetAnchors();
|
|
UpdateAnchors();
|
|
}
|
|
|
|
public abstract void SetRect(float x, float y, float width, float height);
|
|
|
|
private void FindCameraFor(AnchorPoint ap)
|
|
{
|
|
if (ap.target == null || ap.rect != null)
|
|
{
|
|
ap.targetCam = null;
|
|
}
|
|
else
|
|
{
|
|
ap.targetCam = NGUITools.FindCameraForLayer(ap.target.gameObject.layer);
|
|
}
|
|
}
|
|
|
|
public virtual void ParentHasChanged()
|
|
{
|
|
mParentFound = false;
|
|
UIRect uIRect = NGUITools.FindInParents<UIRect>(cachedTransform.parent);
|
|
if (mParent != uIRect)
|
|
{
|
|
if ((bool)mParent)
|
|
{
|
|
mParent.mChildren.Remove(this);
|
|
}
|
|
mParent = uIRect;
|
|
if ((bool)mParent)
|
|
{
|
|
mParent.mChildren.Add(this);
|
|
}
|
|
mRootSet = false;
|
|
}
|
|
}
|
|
|
|
protected abstract void OnStart();
|
|
|
|
protected virtual void OnUpdate()
|
|
{
|
|
}
|
|
}
|