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>
405 lines
13 KiB
C#
405 lines
13 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Cute;
|
|
using UnityEngine;
|
|
|
|
namespace Wizard;
|
|
|
|
public class PurchaseRewardDialog : MonoBehaviour
|
|
{
|
|
public enum Layout
|
|
{
|
|
NORMAL,
|
|
TITLE_BOTTOM,
|
|
TITLE_BOTTOM_LARGE_FONT
|
|
}
|
|
|
|
private readonly Vector3 CARDOBJECT_COLLIDER_SIZE = new Vector3(175f, 230f, 1f);
|
|
|
|
[SerializeField]
|
|
private UIGrid _itemGrid;
|
|
|
|
[SerializeField]
|
|
private GameObject _purchaseRewardItemOriginal;
|
|
|
|
[SerializeField]
|
|
private int _rewardNumParPage;
|
|
|
|
[SerializeField]
|
|
private UIButton _btnPrevPage;
|
|
|
|
[SerializeField]
|
|
private UIButton _btnNextPage;
|
|
|
|
[SerializeField]
|
|
private BoxCollider _flickCollider;
|
|
|
|
[SerializeField]
|
|
private UIPageIndicator _indicator;
|
|
|
|
[SerializeField]
|
|
private GameObject _centerLine;
|
|
|
|
[SerializeField]
|
|
private GameObject _attachPointBottom;
|
|
|
|
private int _currentPageIndex = 1;
|
|
|
|
private int _lastPageIndex = 1;
|
|
|
|
private bool _flickStart;
|
|
|
|
private List<GameObject> _rewardObjectsList = new List<GameObject>();
|
|
|
|
private List<UIBase_CardManager.CardObjData> _cardObjList;
|
|
|
|
private CardDetailUI _cardDetail;
|
|
|
|
protected List<string> _loadedResourceList = new List<string>();
|
|
|
|
private string _detailDialogTitleOverride;
|
|
|
|
private DialogBase _dialog;
|
|
|
|
private Layout _layout;
|
|
|
|
private bool IsFirstPage => _currentPageIndex <= 1;
|
|
|
|
private bool IsLastPage => _currentPageIndex >= _lastPageIndex;
|
|
|
|
private static string GetPrefabPath(List<PurchaseRewardInfo> purchaseRewardsList, Layout layout)
|
|
{
|
|
switch (layout)
|
|
{
|
|
case Layout.TITLE_BOTTOM_LARGE_FONT:
|
|
return "UI/layoutParts/Dialog/PurchaseRewardDialogTitleBottomLargeFont";
|
|
case Layout.TITLE_BOTTOM:
|
|
if (purchaseRewardsList.Count < 10)
|
|
{
|
|
return "UI/layoutParts/Dialog/PurchaseRewardDialogTitleBottom";
|
|
}
|
|
return "UI/layoutParts/Dialog/PurchaseRewardDialog10TitleBottom";
|
|
default:
|
|
if (purchaseRewardsList.Count < 10)
|
|
{
|
|
return "UI/layoutParts/Dialog/PurchaseRewardDialog";
|
|
}
|
|
return "UI/layoutParts/Dialog/PurchaseRewardDialog10";
|
|
}
|
|
}
|
|
|
|
public static DialogBase Create(List<PurchaseRewardInfo> purchaseRewardsList, CardDetailUI cardDetail, bool useLargeDetailDialog, Layout layout, GameObject attachBottomObj = null, string detailDialogTitleOverride = null, bool isPaging = false)
|
|
{
|
|
PurchaseRewardDialog component = (UnityEngine.Object.Instantiate(Resources.Load(GetPrefabPath(purchaseRewardsList, layout))) as GameObject).GetComponent<PurchaseRewardDialog>();
|
|
component.CreateDialog();
|
|
component.Initialize(purchaseRewardsList, cardDetail, useLargeDetailDialog, attachBottomObj, detailDialogTitleOverride, isPaging, layout);
|
|
return component._dialog;
|
|
}
|
|
|
|
private void CreateDialog()
|
|
{
|
|
_dialog = UIManager.GetInstance().CreateDialogClose();
|
|
_dialog.SetSize(DialogBase.Size.M);
|
|
_dialog.SetTitleLabel(Data.SystemText.Get("Shop_0142"));
|
|
_dialog.SetButtonLayout(DialogBase.ButtonLayout.CloseBtn);
|
|
_dialog.SetObj(base.gameObject);
|
|
}
|
|
|
|
private void Initialize(List<PurchaseRewardInfo> purchaseRewardsList, CardDetailUI cardDetail, bool useLargeDetailDialog, GameObject attachBottomObj, string detailDialogTitleOverride, bool isPaging, Layout layout)
|
|
{
|
|
_detailDialogTitleOverride = detailDialogTitleOverride;
|
|
_layout = layout;
|
|
if (attachBottomObj != null)
|
|
{
|
|
attachBottomObj.transform.parent = _attachPointBottom.transform;
|
|
attachBottomObj.transform.localPosition = Vector3.zero;
|
|
attachBottomObj.transform.localScale = Vector3.one;
|
|
}
|
|
_cardDetail = cardDetail;
|
|
InitializePage(purchaseRewardsList);
|
|
UIManager.GetInstance().createInSceneCenterLoading();
|
|
StartCoroutine(LoadResources(purchaseRewardsList, delegate
|
|
{
|
|
SetRewards(purchaseRewardsList, useLargeDetailDialog, isPaging);
|
|
ShowPage(_currentPageIndex);
|
|
UIManager.GetInstance().closeInSceneCenterLoading();
|
|
}));
|
|
}
|
|
|
|
private void SetRewards(List<PurchaseRewardInfo> purchaseRewardsList, bool useLargeDetailDialog, bool isPaging)
|
|
{
|
|
int count = purchaseRewardsList.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
GameObject gameObject = NGUITools.AddChild(_itemGrid.gameObject, _purchaseRewardItemOriginal.gameObject);
|
|
PurchaseRewardInfo purchaseRewardInfo = purchaseRewardsList[i];
|
|
ShopCommonRewardInfo shopCommonRewardInfo = purchaseRewardInfo.RewardInfoList[0];
|
|
PurchaseRewardItem component = gameObject.GetComponent<PurchaseRewardItem>();
|
|
if (shopCommonRewardInfo.Type == 5)
|
|
{
|
|
GameObject cardObj = GetCardObj(shopCommonRewardInfo.UserGoodsId);
|
|
component.SetUserGoods(purchaseRewardInfo, useLargeDetailDialog, cardObj, isPaging, isEnableItemNumber: true);
|
|
}
|
|
else
|
|
{
|
|
component.SetUserGoods(purchaseRewardInfo, useLargeDetailDialog, null, isPaging, _layout != Layout.TITLE_BOTTOM_LARGE_FONT);
|
|
}
|
|
component.DetailDialogTitleOverride = _detailDialogTitleOverride;
|
|
_rewardObjectsList.Add(gameObject);
|
|
}
|
|
if (count <= 4)
|
|
{
|
|
float scale = ((count <= 3) ? 1.5f : 1.25f);
|
|
foreach (GameObject rewardObjects in _rewardObjectsList)
|
|
{
|
|
rewardObjects.GetComponent<PurchaseRewardItem>().SetScale(scale);
|
|
}
|
|
if (count <= 3)
|
|
{
|
|
float num = ((count == 3) ? 1.25f : 1.5f);
|
|
_itemGrid.cellWidth *= num;
|
|
_itemGrid.cellHeight *= num;
|
|
}
|
|
}
|
|
if (_layout == Layout.TITLE_BOTTOM_LARGE_FONT)
|
|
{
|
|
_itemGrid.cellWidth = 275f;
|
|
}
|
|
}
|
|
|
|
private void InitializePage(List<PurchaseRewardInfo> rewardsList)
|
|
{
|
|
_lastPageIndex = (rewardsList.Count - 1) / _rewardNumParPage + 1;
|
|
_currentPageIndex = _lastPageIndex;
|
|
for (int i = 0; i < rewardsList.Count; i++)
|
|
{
|
|
if (!rewardsList[i].IsGet)
|
|
{
|
|
_currentPageIndex = i / _rewardNumParPage + 1;
|
|
break;
|
|
}
|
|
}
|
|
_indicator.Init(_lastPageIndex);
|
|
if (_lastPageIndex > 1)
|
|
{
|
|
_flickCollider.gameObject.SetActive(value: true);
|
|
UIEventListener uIEventListener = UIEventListener.Get(_flickCollider.gameObject);
|
|
uIEventListener.onDragStart = (UIEventListener.VoidDelegate)Delegate.Combine(uIEventListener.onDragStart, new UIEventListener.VoidDelegate(OnDragStart));
|
|
UIEventListener uIEventListener2 = UIEventListener.Get(_flickCollider.gameObject);
|
|
uIEventListener2.onDrag = (UIEventListener.VectorDelegate)Delegate.Combine(uIEventListener2.onDrag, new UIEventListener.VectorDelegate(OnDrag));
|
|
UIEventListener uIEventListener3 = UIEventListener.Get(_btnPrevPage.gameObject);
|
|
uIEventListener3.onClick = (UIEventListener.VoidDelegate)Delegate.Combine(uIEventListener3.onClick, (UIEventListener.VoidDelegate)delegate
|
|
{
|
|
ShowPrevPage();
|
|
});
|
|
UIEventListener uIEventListener4 = UIEventListener.Get(_btnNextPage.gameObject);
|
|
uIEventListener4.onClick = (UIEventListener.VoidDelegate)Delegate.Combine(uIEventListener4.onClick, (UIEventListener.VoidDelegate)delegate
|
|
{
|
|
ShowNextPage();
|
|
});
|
|
}
|
|
if (rewardsList.Count <= 4)
|
|
{
|
|
_itemGrid.transform.localPosition = Vector3.zero;
|
|
_itemGrid.pivot = UIWidget.Pivot.Center;
|
|
_centerLine.SetActive(value: false);
|
|
}
|
|
}
|
|
|
|
private void ShowPage(int pageIndex)
|
|
{
|
|
_currentPageIndex = pageIndex;
|
|
for (int i = 0; i < _rewardObjectsList.Count; i++)
|
|
{
|
|
_rewardObjectsList[i].gameObject.SetActive(value: false);
|
|
}
|
|
int num = (_currentPageIndex - 1) * _rewardNumParPage;
|
|
int num2 = Mathf.Min(num + _rewardNumParPage, _rewardObjectsList.Count);
|
|
for (int j = num; j < num2; j++)
|
|
{
|
|
_rewardObjectsList[j].gameObject.SetActive(value: true);
|
|
}
|
|
_itemGrid.Reposition();
|
|
_btnPrevPage.gameObject.SetActive(!IsFirstPage);
|
|
_btnNextPage.gameObject.SetActive(!IsLastPage);
|
|
_indicator.UpdateIndicator(_currentPageIndex);
|
|
}
|
|
|
|
private void ShowPrevPage()
|
|
{
|
|
if (!IsFirstPage)
|
|
{
|
|
|
|
ShowPage(_currentPageIndex - 1);
|
|
}
|
|
}
|
|
|
|
private void ShowNextPage()
|
|
{
|
|
if (!IsLastPage)
|
|
{
|
|
|
|
ShowPage(_currentPageIndex + 1);
|
|
}
|
|
}
|
|
|
|
private void OnDragStart(GameObject obj)
|
|
{
|
|
_flickStart = true;
|
|
}
|
|
|
|
private void OnDrag(GameObject obj, Vector2 dir)
|
|
{
|
|
if (_flickStart)
|
|
{
|
|
if (dir.x >= 70f)
|
|
{
|
|
_flickStart = false;
|
|
ShowPrevPage();
|
|
}
|
|
else if (dir.x <= -70f)
|
|
{
|
|
_flickStart = false;
|
|
ShowNextPage();
|
|
}
|
|
}
|
|
}
|
|
|
|
private IEnumerator LoadResources(List<PurchaseRewardInfo> purchaseRewardsList, Action onFinish)
|
|
{
|
|
List<string> rewardPathList = new List<string>();
|
|
for (int i = 0; i < purchaseRewardsList.Count; i++)
|
|
{
|
|
for (int j = 0; j < purchaseRewardsList[i].RewardInfoList.Count; j++)
|
|
{
|
|
ShopCommonRewardInfo shopCommonRewardInfo = purchaseRewardsList[i].RewardInfoList[j];
|
|
string text = string.Empty;
|
|
switch ((UserGoods.Type)shopCommonRewardInfo.Type)
|
|
{
|
|
case UserGoods.Type.Item:
|
|
{
|
|
string userGoodsImageName = UserGoods.GetUserGoodsImageName(UserGoods.Type.Item, shopCommonRewardInfo.UserGoodsId);
|
|
text = Toolbox.ResourcesManager.GetAssetTypePath(userGoodsImageName, ResourcesManager.AssetLoadPathType.Item);
|
|
break;
|
|
}
|
|
case UserGoods.Type.Degree:
|
|
text = Toolbox.ResourcesManager.GetAssetTypePath(UserGoods.GetUserGoodsImageName(UserGoods.Type.Degree, 0L), ResourcesManager.AssetLoadPathType.Item);
|
|
break;
|
|
case UserGoods.Type.Sleeve:
|
|
{
|
|
long existingSleeveId = Toolbox.ResourcesManager.GetExistingSleeveId(shopCommonRewardInfo.UserGoodsId);
|
|
text = Toolbox.ResourcesManager.GetAssetTypePath(existingSleeveId.ToString(), ResourcesManager.AssetLoadPathType.SleeveTexture);
|
|
Sleeve sleeve = Data.Master.SleeveMgr.Get(existingSleeveId);
|
|
if (sleeve.IsPremiumSleeve)
|
|
{
|
|
UIManager.GetInstance().getUIBase_CardManager().AddPremireSleevePath(ref rewardPathList, sleeve);
|
|
}
|
|
break;
|
|
}
|
|
case UserGoods.Type.Emblem:
|
|
text = Toolbox.ResourcesManager.GetAssetTypePath(shopCommonRewardInfo.UserGoodsId.ToString(), ResourcesManager.AssetLoadPathType.Emblem_M);
|
|
break;
|
|
}
|
|
if (text != string.Empty && !rewardPathList.Contains(text) && !_loadedResourceList.Contains(text))
|
|
{
|
|
rewardPathList.Add(text);
|
|
}
|
|
}
|
|
}
|
|
yield return StartCoroutine(Toolbox.ResourcesManager.LoadAssetGroupAsync(rewardPathList, null));
|
|
_loadedResourceList.AddRange(rewardPathList);
|
|
yield return StartCoroutine(LoadCardObject(purchaseRewardsList));
|
|
onFinish.Call();
|
|
}
|
|
|
|
private IEnumerator LoadCardObject(List<PurchaseRewardInfo> purchaseRewardsList)
|
|
{
|
|
List<int> cardIdList = GetCardIdList(purchaseRewardsList);
|
|
if (cardIdList.Count != 0)
|
|
{
|
|
UIBase_CardManager uiCardMgr = UIManager.GetInstance().getUIBase_CardManager();
|
|
int layer = LayerMask.NameToLayer("MyPage");
|
|
UIManager.GetInstance().CardLoadSelect(base.gameObject, cardIdList, layer, is2D: true);
|
|
while (!uiCardMgr.getCreateEndFlag() || !uiCardMgr.isAssetAllReady)
|
|
{
|
|
yield return null;
|
|
}
|
|
SetCardObj();
|
|
}
|
|
}
|
|
|
|
private List<int> GetCardIdList(List<PurchaseRewardInfo> purchaseRewardsList)
|
|
{
|
|
List<int> list = new List<int>();
|
|
for (int i = 0; i < purchaseRewardsList.Count; i++)
|
|
{
|
|
PurchaseRewardInfo purchaseRewardInfo = purchaseRewardsList[i];
|
|
for (int j = 0; j < purchaseRewardInfo.RewardInfoList.Count; j++)
|
|
{
|
|
ShopCommonRewardInfo shopCommonRewardInfo = purchaseRewardInfo.RewardInfoList[j];
|
|
if (shopCommonRewardInfo.Type == 5 && !list.Contains((int)shopCommonRewardInfo.UserGoodsId))
|
|
{
|
|
list.Add((int)shopCommonRewardInfo.UserGoodsId);
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private void SetCardObj()
|
|
{
|
|
_cardObjList = UIManager.GetInstance().getCardList2DObjs();
|
|
if (_cardObjList == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < _cardObjList.Count; i++)
|
|
{
|
|
GameObject cardObj = _cardObjList[i].CardObj;
|
|
if (!(null == cardObj))
|
|
{
|
|
cardObj.SetActive(value: false);
|
|
CardListTemplate component = cardObj.GetComponent<CardListTemplate>();
|
|
component.HideNum();
|
|
component._newLabel.gameObject.SetActive(value: false);
|
|
UITexture[] componentsInChildren = cardObj.GetComponentsInChildren<UITexture>(includeInactive: true);
|
|
for (int j = 0; j < componentsInChildren.Length; j++)
|
|
{
|
|
componentsInChildren[j].depth += 23;
|
|
}
|
|
UILabel[] componentsInChildren2 = cardObj.GetComponentsInChildren<UILabel>(includeInactive: true);
|
|
for (int k = 0; k < componentsInChildren2.Length; k++)
|
|
{
|
|
componentsInChildren2[k].depth += 23;
|
|
}
|
|
component._frameSprite.depth = 23;
|
|
component._cardTexture.depth = 21;
|
|
cardObj.AddComponent<BoxCollider>().size = CARDOBJECT_COLLIDER_SIZE;
|
|
UIEventListener.Get(cardObj).onClick = _cardDetail.OnPushCardDetailOn;
|
|
if (_lastPageIndex > 1)
|
|
{
|
|
UIEventListener uIEventListener = UIEventListener.Get(cardObj);
|
|
uIEventListener.onDragStart = (UIEventListener.VoidDelegate)Delegate.Combine(uIEventListener.onDragStart, new UIEventListener.VoidDelegate(OnDragStart));
|
|
UIEventListener uIEventListener2 = UIEventListener.Get(cardObj);
|
|
uIEventListener2.onDrag = (UIEventListener.VectorDelegate)Delegate.Combine(uIEventListener2.onDrag, new UIEventListener.VectorDelegate(OnDrag));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private GameObject GetCardObj(long userGoodsId)
|
|
{
|
|
GameObject result = null;
|
|
for (int i = 0; i < _cardObjList.Count; i++)
|
|
{
|
|
if (_cardObjList[i] != null && userGoodsId == _cardObjList[i].ids)
|
|
{
|
|
result = _cardObjList[i].CardObj;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|