2024-12-09 17:02:39 +08:00
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using TMPro;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
using UnityEngine.UIElements;
|
|
|
|
|
|
using Button = UnityEngine.UI.Button;
|
|
|
|
|
|
using Image = UnityEngine.UI.Image;
|
|
|
|
|
|
using Toggle = UnityEngine.UI.Toggle;
|
|
|
|
|
|
using PhxhSDK;
|
|
|
|
|
|
using Framework;
|
2024-12-11 11:16:11 +08:00
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
|
|
using Gameplay.Common;
|
|
|
|
|
|
using cfg.VihecleCultivateCfg;
|
2024-12-12 19:11:59 +08:00
|
|
|
|
using Gameplay.Net;
|
|
|
|
|
|
using Gameplay;
|
2024-12-09 17:02:39 +08:00
|
|
|
|
|
|
|
|
|
|
public class UI_VehicleTechTreeController : UIWindow
|
|
|
|
|
|
{
|
|
|
|
|
|
//UI GameObj
|
|
|
|
|
|
///Auto Gen Start///
|
|
|
|
|
|
public Image Image_Bg;
|
|
|
|
|
|
public Image Image_LeftPanelBg;
|
|
|
|
|
|
public Button Button_Back;
|
|
|
|
|
|
public TMP_Text Text_Back;
|
|
|
|
|
|
public Toggle Toggle_0;
|
|
|
|
|
|
public Toggle Toggle_1;
|
|
|
|
|
|
public Toggle Toggle_2;
|
|
|
|
|
|
public Toggle Toggle_3;
|
|
|
|
|
|
public Toggle Toggle_4;
|
|
|
|
|
|
public Toggle Toggle_5;
|
|
|
|
|
|
public Image Image_TitleIcon;
|
|
|
|
|
|
public TMP_Text Text_Title;
|
|
|
|
|
|
public TMP_Text Text_TitleEn0;
|
|
|
|
|
|
public TMP_Text Text_TitleEn1;
|
|
|
|
|
|
public PhxhScrollView ScrollView_TechTree;
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-12 19:11:59 +08:00
|
|
|
|
///Auto Gen End///
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-12 19:11:59 +08:00
|
|
|
|
VehicleType curType = VehicleType.None;
|
2024-12-11 11:16:11 +08:00
|
|
|
|
VehicleType CurType
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return curType;
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
curType = value;
|
2025-04-14 16:32:48 +08:00
|
|
|
|
Text_Title.text = VehicleDataHelper.GetVehicleOrganization(curType);//CommonUtils.GetLocalizeText("VehicleOrg_" + curType);
|
2024-12-16 12:04:37 +08:00
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
List<Toggle> Toggles;
|
|
|
|
|
|
Dictionary<VehicleType, GameObject> TreePrefabs;//= new();
|
2024-12-09 17:02:39 +08:00
|
|
|
|
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
public async static UniTask<UIWindow> Open(object data = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await UIManager.Instance.CreateAndOpenWindow(UINameConst.UI_VehicleTechTree, data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
// deal with input data
|
|
|
|
|
|
public override void PreLoad(object data = null)
|
2024-12-11 11:16:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
TreePrefabs = new Dictionary<VehicleType, GameObject>();
|
|
|
|
|
|
var VehicleTypeCount = (int)VehicleType.Max - 1;
|
|
|
|
|
|
for (int i = 0; i < VehicleTypeCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var type = (VehicleType)(i + 1);
|
|
|
|
|
|
var path = string.Format(UIManager.UI_PREFAB_PATH, "Interface_Vehicle/TechTree/" + type.ToString() + "_0");
|
|
|
|
|
|
PostPreload<GameObject>(path).Forget();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
// Use this for initialization
|
|
|
|
|
|
public override void OnInit()
|
|
|
|
|
|
{
|
|
|
|
|
|
//bind UI GameObj
|
|
|
|
|
|
UI_VehicleTechTreeBinder.GetComponents(this);
|
2024-12-11 11:16:11 +08:00
|
|
|
|
ButtonBind();
|
|
|
|
|
|
|
|
|
|
|
|
var currencyList = GetComponent<UI_CurrencyList>("UI_CurrencyList");
|
2025-04-25 14:57:32 +08:00
|
|
|
|
currencyList.SetData(GLConfig.Inst.data.DiamondItemId, 12);//GLConfig.Inst.data.GoldItemId
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
Toggles = new List<Toggle> { Toggle_0, Toggle_1, Toggle_2, Toggle_3, Toggle_4, Toggle_5 };
|
|
|
|
|
|
var VehicleTypeCount = (int)VehicleType.Max - 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < VehicleTypeCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var toggle = Toggles[i];
|
|
|
|
|
|
var type = (VehicleType)(i + 1);
|
|
|
|
|
|
toggle.onValueChanged.AddListener((isOn) => OnToggleValueChanged(toggle, type, isOn));
|
|
|
|
|
|
SetTreePrefab(type);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int i = VehicleTypeCount; i < Toggles.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Toggles[i].gameObject.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
Toggles[0].isOn = true;
|
2024-12-17 15:00:40 +08:00
|
|
|
|
|
|
|
|
|
|
EventManager.Instance.Register(EventManager.EventName.CharacterInfoRefresh, OnCharacterDataRefresh);
|
2024-12-31 12:55:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//invoke when open window
|
|
|
|
|
|
protected override void OnShowWindow(object data = null)
|
|
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
var currencyList = GetComponent<UI_CurrencyList>("UI_CurrencyList");
|
|
|
|
|
|
currencyList.Refresh();
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
//invoke when reload window
|
|
|
|
|
|
protected override void OnReloadWindow(object data = null)
|
|
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
//invoke when close window
|
|
|
|
|
|
protected override void OnHideWindow()
|
|
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2024-12-09 17:02:39 +08:00
|
|
|
|
//invoke when destroy
|
|
|
|
|
|
public override void OnRelease()
|
|
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
base.OnRelease();
|
2025-01-16 15:00:19 +08:00
|
|
|
|
//EventManager.Instance.Send(EventManager.EventName.EN_UIControllCultivateMainInput, true);
|
2024-12-13 14:56:43 +08:00
|
|
|
|
CommonUtils.ScreenshotReset();
|
|
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
2025-01-24 15:47:19 +08:00
|
|
|
|
void SetTreePrefab(VehicleType type)
|
2024-12-11 11:16:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
GameObject uiPrefab = null;
|
|
|
|
|
|
var path = string.Format(UIManager.UI_PREFAB_PATH, "Interface_Vehicle/TechTree/" + type.ToString() + "_0");
|
|
|
|
|
|
var res = GetPreLoadResult<GameObject>(path);
|
|
|
|
|
|
if (res != null)
|
2025-01-24 15:47:19 +08:00
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
uiPrefab = GameObject.Instantiate(GetPreLoadResult<GameObject>(path), ScrollView_TechTree.content, false);
|
2025-01-24 15:47:19 +08:00
|
|
|
|
uiPrefab.SetActive(false);
|
|
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
TreePrefabs.Add(type, uiPrefab);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
BindTreeButton(uiPrefab, type);
|
2024-12-11 11:16:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-17 15:00:40 +08:00
|
|
|
|
void OnCharacterDataRefresh()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TreePrefabs.TryGetValue(curType, out GameObject go) && go != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
go.SetActive(true);
|
|
|
|
|
|
RefreshTreeContent(go, curType);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-11 11:16:11 +08:00
|
|
|
|
void OnToggleValueChanged(Toggle changeToggle, VehicleType type, bool isOn)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isOn)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var toggle in Toggles)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (toggle != changeToggle)
|
|
|
|
|
|
{
|
|
|
|
|
|
toggle.isOn = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
CurType = type;
|
|
|
|
|
|
|
|
|
|
|
|
if (TreePrefabs.TryGetValue(type, out GameObject go) && go != null)
|
2024-12-12 19:11:59 +08:00
|
|
|
|
{
|
2024-12-11 11:16:11 +08:00
|
|
|
|
go.SetActive(true);
|
2025-01-15 15:25:23 +08:00
|
|
|
|
ScrollView_TechTree.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, go.GetComponent<RectTransform>().sizeDelta.y);
|
|
|
|
|
|
ScrollView_TechTree.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, go.GetComponent<RectTransform>().sizeDelta.x);
|
|
|
|
|
|
|
2024-12-12 19:11:59 +08:00
|
|
|
|
RefreshTreeContent(go, type);
|
|
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var isOnCnt = 0;
|
|
|
|
|
|
foreach (var toggle in Toggles)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (toggle.isOn)
|
|
|
|
|
|
{
|
|
|
|
|
|
isOnCnt++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isOnCnt == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
changeToggle.isOn = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (TreePrefabs.TryGetValue(type, out GameObject go) && go != null)
|
|
|
|
|
|
go.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-12 19:11:59 +08:00
|
|
|
|
void BindTreeButton(GameObject Tree, VehicleType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Tree == null)
|
|
|
|
|
|
return;
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
var root = VehicleCultivateManager.Instance.GetVehicleForest(type);
|
|
|
|
|
|
Queue<VehicleTreeNode> queue = new Queue<VehicleTreeNode>();
|
2024-12-12 19:11:59 +08:00
|
|
|
|
foreach (var child in root.VehicleChildren)
|
2024-12-11 11:16:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
var node = VehicleCultivateManager.Instance.Get(child) as VehicleTreeNode;
|
|
|
|
|
|
queue.Enqueue(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (queue.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = queue.Dequeue();
|
2024-12-12 19:11:59 +08:00
|
|
|
|
Transform childGO = Tree.transform.Find("v_" + node.ID);
|
|
|
|
|
|
if (childGO == null)
|
2024-12-11 11:16:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Can't find node " + node.ID);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2024-12-12 19:11:59 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = Actor.Instance.Character.GetVehicleDevelopment(node.ID);
|
|
|
|
|
|
Button btn = CommonUtils.GetComponent<Button>(childGO.gameObject);
|
|
|
|
|
|
|
|
|
|
|
|
if (btn != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
BindButton(btn, () => VehicleNodeClick(node));
|
|
|
|
|
|
}
|
|
|
|
|
|
var children = VehicleCultivateManager.Instance.GetVehicleChildren(node.ID);
|
|
|
|
|
|
foreach (var child in children)
|
|
|
|
|
|
{
|
|
|
|
|
|
queue.Enqueue(child);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
}
|
2024-12-12 19:11:59 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void VehicleNodeClick(VehicleTreeNode node)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (node == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("VehicleNodeClick node is null");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-16 12:04:37 +08:00
|
|
|
|
var cfg = TableManager.Instance.Tables.VehicleConfig.GetOrDefault((int)node.ID);
|
|
|
|
|
|
if(cfg == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("VehicleNode cfg is null");
|
|
|
|
|
|
return;
|
2025-04-25 16:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (cfg.HideInTree)
|
|
|
|
|
|
{
|
|
|
|
|
|
CommonUtils.HideVehicleTip();
|
|
|
|
|
|
return;
|
2024-12-16 12:04:37 +08:00
|
|
|
|
}
|
2024-12-12 19:11:59 +08:00
|
|
|
|
var data = Actor.Instance.Character.GetVehicleDevelopment(node.ID);
|
|
|
|
|
|
if (data != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.Log("VehicleNodeClick " + node.ID + " is unlocked");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-12-16 12:04:37 +08:00
|
|
|
|
var UnlockMethod = cfg.UnlockType;
|
|
|
|
|
|
DebugUtil.Log("VehicleNode " + node.ID + " is locked, unlock method is " + UnlockMethod);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
}
|
2024-12-31 12:55:44 +08:00
|
|
|
|
UI_VehiclePartTreeController.Open(node.ID).Forget();
|
2024-12-12 19:11:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RefreshTreeContent(GameObject Tree, VehicleType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Tree == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
var root = VehicleCultivateManager.Instance.GetVehicleForest(type);
|
|
|
|
|
|
Queue<VehicleTreeNode> queue = new Queue<VehicleTreeNode>();
|
|
|
|
|
|
foreach (var child in root.VehicleChildren)
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = VehicleCultivateManager.Instance.Get(child) as VehicleTreeNode;
|
|
|
|
|
|
queue.Enqueue(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (queue.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = queue.Dequeue();
|
2024-12-16 12:04:37 +08:00
|
|
|
|
var childGO = CommonUtils.GetGameObject(Tree, "v_" + node.ID);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
if (childGO == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Can't find node " + node.ID);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-12-16 12:04:37 +08:00
|
|
|
|
//<2F>༭<EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>ʾ
|
|
|
|
|
|
|
|
|
|
|
|
#region <20><><EFBFBD><EFBFBD>״̬
|
|
|
|
|
|
var data = Actor.Instance.Character.GetVehicleDevelopment(node.ID);
|
|
|
|
|
|
var tag = CommonUtils.GetGameObject(childGO, "Image_Unlock");
|
2024-12-12 19:11:59 +08:00
|
|
|
|
|
2024-12-18 19:58:14 +08:00
|
|
|
|
var costTag = CommonUtils.GetGameObject(childGO, "Image_PriceBg");
|
2024-12-20 13:34:47 +08:00
|
|
|
|
var costText = CommonUtils.GetComponent<TMP_Text>(childGO, "Text_Price");
|
2024-12-18 19:58:14 +08:00
|
|
|
|
|
2024-12-20 13:34:47 +08:00
|
|
|
|
var cfg = TableManager.Instance.Tables.VehicleConfig.GetOrDefault((int)node.ID);
|
|
|
|
|
|
if (cfg != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
costText.text = cfg.UnlockCost[0].Count.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (data != null)
|
2024-12-12 19:11:59 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (tag != null)
|
2024-12-18 19:58:14 +08:00
|
|
|
|
tag.SetActive(true);
|
|
|
|
|
|
if (costTag != null)
|
|
|
|
|
|
costTag.SetActive(false);
|
|
|
|
|
|
if(costText != null)
|
2024-12-20 13:34:47 +08:00
|
|
|
|
costText.gameObject.SetActive(false);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tag != null)
|
2024-12-18 19:58:14 +08:00
|
|
|
|
tag.SetActive(false);
|
|
|
|
|
|
if (costTag != null)
|
|
|
|
|
|
costTag.SetActive(true);
|
|
|
|
|
|
if(costText != null)
|
2024-12-20 13:34:47 +08:00
|
|
|
|
costText.gameObject.SetActive(true);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
}
|
2024-12-20 13:34:47 +08:00
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 12:04:37 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region <20>ȼ<EFBFBD>
|
|
|
|
|
|
var Text_level = CommonUtils.GetComponent<TMP_Text>(childGO.gameObject, "Text_Level");
|
|
|
|
|
|
if (Text_level != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var vehicleLevel = 1;
|
|
|
|
|
|
if (TableManager.Instance.Tables.VehicleConfig.DataMap.ContainsKey((int)node.ID))
|
|
|
|
|
|
vehicleLevel = TableManager.Instance.Tables.VehicleConfig.DataMap[(int)node.ID].VehicleLevel;
|
|
|
|
|
|
Text_level.text = CommonUtils.GetRomanCode(vehicleLevel);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-01-08 15:41:43 +08:00
|
|
|
|
#region <20><><EFBFBD>ػ<EFBFBD><D8BB><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
var Text_Name = CommonUtils.GetComponent<TMP_Text>(childGO.gameObject, "Text_Name");
|
|
|
|
|
|
if (Text_Name != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Text_Name.text=VehicleDataHelper.GetVehicleLocalizeName((int)node.ID);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2024-12-16 12:04:37 +08:00
|
|
|
|
var children = VehicleCultivateManager.Instance.GetVehicleChildren(node.ID);
|
2024-12-12 19:11:59 +08:00
|
|
|
|
foreach (var child in children)
|
|
|
|
|
|
{
|
|
|
|
|
|
queue.Enqueue(child);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
void ButtonBind()
|
|
|
|
|
|
{
|
|
|
|
|
|
BindButton(Button_Back, OnClickBack);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region Button Click
|
|
|
|
|
|
void OnClickBack()
|
|
|
|
|
|
{
|
|
|
|
|
|
CloseWindow();
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|
2024-12-11 11:16:11 +08:00
|
|
|
|
#endregion
|
2024-12-09 17:02:39 +08:00
|
|
|
|
}
|