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>
476 lines
9.9 KiB
C#
476 lines
9.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
[AddComponentMenu("NGUI/UI/Atlas")]
|
|
public class UIAtlas : MonoBehaviour
|
|
{
|
|
[Serializable]
|
|
private class Sprite
|
|
{
|
|
public string name = "Unity Bug";
|
|
|
|
public Rect outer = new Rect(0f, 0f, 1f, 1f);
|
|
|
|
public Rect inner = new Rect(0f, 0f, 1f, 1f);
|
|
|
|
public float paddingLeft;
|
|
|
|
public float paddingRight;
|
|
|
|
public float paddingTop;
|
|
|
|
public float paddingBottom;
|
|
}
|
|
|
|
private enum Coordinates
|
|
{
|
|
TexCoords
|
|
}
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private Material material;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private List<UISpriteData> mSprites = new List<UISpriteData>();
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private float mPixelSize = 1f;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private UIAtlas mReplacement;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private Coordinates mCoordinates;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private List<Sprite> sprites = new List<Sprite>();
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private int width;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private int height;
|
|
|
|
private int mPMA = -1;
|
|
|
|
private Dictionary<string, int> mSpriteIndices = new Dictionary<string, int>();
|
|
|
|
public Material spriteMaterial
|
|
{
|
|
get
|
|
{
|
|
if (!(mReplacement != null))
|
|
{
|
|
return material;
|
|
}
|
|
return mReplacement.spriteMaterial;
|
|
}
|
|
set
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
mReplacement.spriteMaterial = value;
|
|
return;
|
|
}
|
|
if (material == null)
|
|
{
|
|
mPMA = 0;
|
|
material = value;
|
|
return;
|
|
}
|
|
MarkAsChanged();
|
|
mPMA = -1;
|
|
material = value;
|
|
MarkAsChanged();
|
|
}
|
|
}
|
|
|
|
public int atlasWidth => width;
|
|
|
|
public int atlasHeight => height;
|
|
|
|
public bool premultipliedAlpha
|
|
{
|
|
get
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
return mReplacement.premultipliedAlpha;
|
|
}
|
|
if (mPMA == -1)
|
|
{
|
|
Material material = spriteMaterial;
|
|
mPMA = ((material != null && material.shader != null && material.shader.name.Contains("Premultiplied")) ? 1 : 0);
|
|
}
|
|
return mPMA == 1;
|
|
}
|
|
}
|
|
|
|
public List<UISpriteData> spriteList
|
|
{
|
|
get
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
return mReplacement.spriteList;
|
|
}
|
|
if (mSprites.Count == 0)
|
|
{
|
|
Upgrade();
|
|
}
|
|
return mSprites;
|
|
}
|
|
set
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
mReplacement.spriteList = value;
|
|
}
|
|
else
|
|
{
|
|
mSprites = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Texture texture
|
|
{
|
|
get
|
|
{
|
|
if (!(mReplacement != null))
|
|
{
|
|
if (!(material != null))
|
|
{
|
|
return null;
|
|
}
|
|
return material.mainTexture;
|
|
}
|
|
return mReplacement.texture;
|
|
}
|
|
}
|
|
|
|
public float pixelSize
|
|
{
|
|
get
|
|
{
|
|
if (!(mReplacement != null))
|
|
{
|
|
return mPixelSize;
|
|
}
|
|
return mReplacement.pixelSize;
|
|
}
|
|
set
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
mReplacement.pixelSize = value;
|
|
return;
|
|
}
|
|
float num = Mathf.Clamp(value, 0.25f, 4f);
|
|
if (mPixelSize != num)
|
|
{
|
|
mPixelSize = num;
|
|
MarkAsChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public UIAtlas replacement
|
|
{
|
|
get
|
|
{
|
|
return mReplacement;
|
|
}
|
|
set
|
|
{
|
|
UIAtlas uIAtlas = value;
|
|
if (uIAtlas == this)
|
|
{
|
|
uIAtlas = null;
|
|
}
|
|
if (mReplacement != uIAtlas)
|
|
{
|
|
if (uIAtlas != null && uIAtlas.replacement == this)
|
|
{
|
|
uIAtlas.replacement = null;
|
|
}
|
|
if (mReplacement != null)
|
|
{
|
|
MarkAsChanged();
|
|
}
|
|
mReplacement = uIAtlas;
|
|
if (uIAtlas != null)
|
|
{
|
|
material = null;
|
|
}
|
|
MarkAsChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public UISpriteData GetSprite(string name)
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
return mReplacement.GetSprite(name);
|
|
}
|
|
if (!string.IsNullOrEmpty(name))
|
|
{
|
|
if (mSprites.Count == 0)
|
|
{
|
|
Upgrade();
|
|
}
|
|
if (mSprites.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
if (mSpriteIndices.Count != mSprites.Count)
|
|
{
|
|
MarkSpriteListAsChanged();
|
|
}
|
|
if (mSpriteIndices.TryGetValue(name, out var value))
|
|
{
|
|
if (value > -1 && value < mSprites.Count)
|
|
{
|
|
return mSprites[value];
|
|
}
|
|
MarkSpriteListAsChanged();
|
|
if (!mSpriteIndices.TryGetValue(name, out value))
|
|
{
|
|
return null;
|
|
}
|
|
return mSprites[value];
|
|
}
|
|
int i = 0;
|
|
for (int count = mSprites.Count; i < count; i++)
|
|
{
|
|
UISpriteData uISpriteData = mSprites[i];
|
|
if (!string.IsNullOrEmpty(uISpriteData.name) && name == uISpriteData.name)
|
|
{
|
|
MarkSpriteListAsChanged();
|
|
return uISpriteData;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void MarkSpriteListAsChanged()
|
|
{
|
|
mSpriteIndices.Clear();
|
|
int i = 0;
|
|
for (int count = mSprites.Count; i < count; i++)
|
|
{
|
|
mSpriteIndices[mSprites[i].name] = i;
|
|
}
|
|
}
|
|
|
|
public BetterList<string> GetListOfSprites()
|
|
{
|
|
if (mReplacement != null)
|
|
{
|
|
return mReplacement.GetListOfSprites();
|
|
}
|
|
if (mSprites.Count == 0)
|
|
{
|
|
Upgrade();
|
|
}
|
|
BetterList<string> betterList = new BetterList<string>();
|
|
int i = 0;
|
|
for (int count = mSprites.Count; i < count; i++)
|
|
{
|
|
UISpriteData uISpriteData = mSprites[i];
|
|
if (uISpriteData != null && !string.IsNullOrEmpty(uISpriteData.name))
|
|
{
|
|
betterList.Add(uISpriteData.name);
|
|
}
|
|
}
|
|
return betterList;
|
|
}
|
|
|
|
public BetterList<string> GetListOfSprites(string match)
|
|
{
|
|
if ((bool)mReplacement)
|
|
{
|
|
return mReplacement.GetListOfSprites(match);
|
|
}
|
|
if (string.IsNullOrEmpty(match))
|
|
{
|
|
return GetListOfSprites();
|
|
}
|
|
if (mSprites.Count == 0)
|
|
{
|
|
Upgrade();
|
|
}
|
|
BetterList<string> betterList = new BetterList<string>();
|
|
int i = 0;
|
|
for (int count = mSprites.Count; i < count; i++)
|
|
{
|
|
UISpriteData uISpriteData = mSprites[i];
|
|
if (uISpriteData != null && !string.IsNullOrEmpty(uISpriteData.name) && string.Equals(match, uISpriteData.name, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
betterList.Add(uISpriteData.name);
|
|
return betterList;
|
|
}
|
|
}
|
|
string[] array = match.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
for (int j = 0; j < array.Length; j++)
|
|
{
|
|
array[j] = array[j].ToLower();
|
|
}
|
|
int k = 0;
|
|
for (int count2 = mSprites.Count; k < count2; k++)
|
|
{
|
|
UISpriteData uISpriteData2 = mSprites[k];
|
|
if (uISpriteData2 == null || string.IsNullOrEmpty(uISpriteData2.name))
|
|
{
|
|
continue;
|
|
}
|
|
string text = uISpriteData2.name.ToLower();
|
|
int num = 0;
|
|
for (int l = 0; l < array.Length; l++)
|
|
{
|
|
if (text.Contains(array[l]))
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
if (num == array.Length)
|
|
{
|
|
betterList.Add(uISpriteData2.name);
|
|
}
|
|
}
|
|
return betterList;
|
|
}
|
|
|
|
private bool References(UIAtlas atlas)
|
|
{
|
|
if (atlas == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (atlas == this)
|
|
{
|
|
return true;
|
|
}
|
|
if (!(mReplacement != null))
|
|
{
|
|
return false;
|
|
}
|
|
return mReplacement.References(atlas);
|
|
}
|
|
|
|
public static bool CheckIfRelated(UIAtlas a, UIAtlas b)
|
|
{
|
|
if (a == null || b == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!(a == b) && !a.References(b))
|
|
{
|
|
return b.References(a);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void MarkAsChanged()
|
|
{
|
|
if (this == null)
|
|
{
|
|
return;
|
|
}
|
|
if (mReplacement != null)
|
|
{
|
|
mReplacement.MarkAsChanged();
|
|
}
|
|
UISprite[] array = NGUITools.FindActive<UISprite>();
|
|
int i = 0;
|
|
for (int num = array.Length; i < num; i++)
|
|
{
|
|
UISprite uISprite = array[i];
|
|
if (CheckIfRelated(this, uISprite.atlas))
|
|
{
|
|
UIAtlas atlas = uISprite.atlas;
|
|
uISprite.atlas = null;
|
|
uISprite.atlas = atlas;
|
|
}
|
|
}
|
|
UIFont[] array2 = Resources.FindObjectsOfTypeAll(typeof(UIFont)) as UIFont[];
|
|
int j = 0;
|
|
for (int num2 = array2.Length; j < num2; j++)
|
|
{
|
|
UIFont uIFont = array2[j];
|
|
if (CheckIfRelated(this, uIFont.atlas))
|
|
{
|
|
UIAtlas atlas2 = uIFont.atlas;
|
|
uIFont.atlas = null;
|
|
uIFont.atlas = atlas2;
|
|
}
|
|
}
|
|
UILabel[] array3 = NGUITools.FindActive<UILabel>();
|
|
int k = 0;
|
|
for (int num3 = array3.Length; k < num3; k++)
|
|
{
|
|
UILabel uILabel = array3[k];
|
|
if (uILabel.bitmapFont != null && CheckIfRelated(this, uILabel.bitmapFont.atlas))
|
|
{
|
|
UIFont bitmapFont = uILabel.bitmapFont;
|
|
uILabel.bitmapFont = null;
|
|
uILabel.bitmapFont = bitmapFont;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool Upgrade()
|
|
{
|
|
if ((bool)mReplacement)
|
|
{
|
|
return mReplacement.Upgrade();
|
|
}
|
|
if (mSprites.Count == 0 && sprites.Count > 0 && (bool)material)
|
|
{
|
|
Texture mainTexture = material.mainTexture;
|
|
int num = ((mainTexture != null) ? mainTexture.width : 512);
|
|
int num2 = ((mainTexture != null) ? mainTexture.height : 512);
|
|
for (int i = 0; i < sprites.Count; i++)
|
|
{
|
|
Sprite sprite = sprites[i];
|
|
Rect outer = sprite.outer;
|
|
Rect inner = sprite.inner;
|
|
if (mCoordinates == Coordinates.TexCoords)
|
|
{
|
|
NGUIMath.ConvertToPixels(outer, num, num2, round: true);
|
|
NGUIMath.ConvertToPixels(inner, num, num2, round: true);
|
|
}
|
|
UISpriteData uISpriteData = new UISpriteData();
|
|
uISpriteData.name = sprite.name;
|
|
uISpriteData.x = Mathf.RoundToInt(outer.xMin);
|
|
uISpriteData.y = Mathf.RoundToInt(outer.yMin);
|
|
uISpriteData.width = Mathf.RoundToInt(outer.width);
|
|
uISpriteData.height = Mathf.RoundToInt(outer.height);
|
|
uISpriteData.paddingLeft = Mathf.RoundToInt(sprite.paddingLeft * outer.width);
|
|
uISpriteData.paddingRight = Mathf.RoundToInt(sprite.paddingRight * outer.width);
|
|
uISpriteData.paddingBottom = Mathf.RoundToInt(sprite.paddingBottom * outer.height);
|
|
uISpriteData.paddingTop = Mathf.RoundToInt(sprite.paddingTop * outer.height);
|
|
uISpriteData.borderLeft = Mathf.RoundToInt(inner.xMin - outer.xMin);
|
|
uISpriteData.borderRight = Mathf.RoundToInt(outer.xMax - inner.xMax);
|
|
uISpriteData.borderBottom = Mathf.RoundToInt(outer.yMax - inner.yMax);
|
|
uISpriteData.borderTop = Mathf.RoundToInt(inner.yMin - outer.yMin);
|
|
mSprites.Add(uISpriteData);
|
|
}
|
|
sprites.Clear();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|