From 8413c2688b1e1cbb6587cf6e75c5fa9a95fe0cee Mon Sep 17 00:00:00 2001 From: huangjun Date: Tue, 1 Sep 2026 10:20:02 +0800 Subject: [PATCH] =?UTF-8?q?M0=20=E5=9B=9E=E5=90=88=E5=88=B6=E6=88=98?= =?UTF-8?q?=E6=96=97=E5=A3=B3=E5=B1=82=EF=BC=9A=E6=8E=A5=E5=8F=A3=E4=B8=8E?= =?UTF-8?q?=E5=8D=95=E5=B1=80=E7=8A=B6=E6=80=81=E3=80=81=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E3=80=81=E6=9A=82=E5=81=9C=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/GameMain/DataTables/tbformattext.bytes | 2 +- Assets/GameMain/DataTables/tbformattext.json | 4 + .../GameMain/DataTables/tbuiformconfig.bytes | Bin 88 -> 110 bytes .../GameMain/DataTables/tbuiformconfig.json | 7 + .../GameMain/Scripts/Base/Gen/UIFormType.cs | 4 + .../Scripts/Editor/BattleUIPrefabBuilder.cs | 592 ++ .../Editor/BattleUIPrefabBuilder.cs.meta | 11 + .../Scripts/Editor/SepCore.Editor.asmdef | 5 +- .../Scripts/Procedure/Base/ProcedureLaunch.cs | 6 + .../Scripts/Runtime/Base/GameEntry.Custom.cs | 12 + Assets/GameMain/Scripts/Runtime/Battle.meta | 8 + .../Scripts/Runtime/Battle/BattleAdvance.cs | 178 + .../Runtime/Battle/BattleAdvance.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleCommand.cs | 52 + .../Runtime/Battle/BattleCommand.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleEncounter.cs | 31 + .../Runtime/Battle/BattleEncounter.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleFaction.cs | 18 + .../Runtime/Battle/BattleFaction.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleFlowState.cs | 18 + .../Runtime/Battle/BattleFlowState.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleOutcome.cs | 28 + .../Runtime/Battle/BattleOutcome.cs.meta | 11 + .../Runtime/Battle/BattlePlayerInput.cs | 80 + .../Runtime/Battle/BattlePlayerInput.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleResult.cs | 84 + .../Runtime/Battle/BattleResult.cs.meta | 11 + .../Runtime/Battle/BattleReturnPlan.cs | 82 + .../Runtime/Battle/BattleReturnPlan.cs.meta | 11 + .../Scripts/Runtime/Battle/BattleSnapshot.cs | 223 + .../Runtime/Battle/BattleSnapshot.cs.meta | 11 + .../Runtime/Battle/BattleStartRequest.cs | 58 + .../Runtime/Battle/BattleStartRequest.cs.meta | 11 + .../Runtime/Battle/IBattleConfigProvider.cs | 32 + .../Battle/IBattleConfigProvider.cs.meta | 11 + .../Runtime/Battle/IEnemyBattlePolicy.cs | 20 + .../Runtime/Battle/IEnemyBattlePolicy.cs.meta | 11 + .../Battle/IExplorationBattleBridge.cs | 16 + .../Battle/IExplorationBattleBridge.cs.meta | 11 + .../Runtime/CustomComponent/Random.meta | 8 + .../Random/IRunRandomSource.cs | 20 + .../Random/IRunRandomSource.cs.meta | 11 + .../CustomComponent/Random/RandomComponent.cs | 46 + .../Random/RandomComponent.cs.meta | 11 + .../CustomComponent/Random/RunRandomSource.cs | 32 + .../Random/RunRandomSource.cs.meta | 11 + .../Scripts/Runtime/CustomComponent/Run.meta | 8 + .../Run/RunBattleCoordinator.cs | 112 + .../Run/RunBattleCoordinator.cs.meta | 11 + .../CustomComponent/Run/RunPlayerState.cs | 64 + .../Run/RunPlayerState.cs.meta | 11 + .../Run/TurnBattleComponent.cs | 136 + .../Run/TurnBattleComponent.cs.meta | 11 + .../Runtime/Debugger/BattleDebuggerWindow.cs | 144 + .../Debugger/BattleDebuggerWindow.cs.meta | 11 + Assets/GameMain/Scripts/UI/Battle.meta | 8 + .../Scripts/UI/Battle/BattleActorCardItem.cs | 40 + .../UI/Battle/BattleActorCardItem.cs.meta | 11 + .../Scripts/UI/Battle/BattleEnemySlotItem.cs | 37 + .../UI/Battle/BattleEnemySlotItem.cs.meta | 11 + .../Scripts/UI/Battle/BattleForm.Logic.cs | 64 + .../UI/Battle/BattleForm.Logic.cs.meta | 11 + .../GameMain/Scripts/UI/Battle/BattleForm.cs | 13 + .../Scripts/UI/Battle/BattleForm.cs.meta | 11 + .../Scripts/UI/Battle/BattleTurnSlotItem.cs | 27 + .../UI/Battle/BattleTurnSlotItem.cs.meta | 11 + .../GameMain/Scripts/UI/Battle/BattleView.cs | 23 + .../Scripts/UI/Battle/BattleView.cs.meta | 11 + .../UI/Inventory/InventoryForm.Logic.cs | 2 - Assets/GameMain/UI/UIForms/BattleForm.prefab | 7357 +++++++++++++++++ .../UI/UIForms/BattleForm.prefab.meta | 7 + Assets/GameMain/UI/UIForms/LobbyForm.prefab | 2 +- .../UI/UIItems/BattleEnemySlotItem.prefab | 745 ++ .../UIItems/BattleEnemySlotItem.prefab.meta | 7 + .../UI/UIItems/BattleTurnSlotItem.prefab | 441 + .../UI/UIItems/BattleTurnSlotItem.prefab.meta | 7 + Assets/Launcher.unity | 92 +- .../Tech/04_TurnBasedCombatDevelopmentTODO.md | 727 ++ Docs/Tech/README.md | 3 +- 数据表/Datas/FormatText_格式化文本.xlsx | Bin 9863 -> 9903 bytes 数据表/Datas/UIFormConfig_界面配置.xlsx | Bin 9873 -> 9896 bytes 数据表/Datas/__enums__.xlsx | Bin 13745 -> 16302 bytes 82 files changed, 12032 insertions(+), 8 deletions(-) create mode 100644 Assets/GameMain/Scripts/Editor/BattleUIPrefabBuilder.cs create mode 100644 Assets/GameMain/Scripts/Editor/BattleUIPrefabBuilder.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleAdvance.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleAdvance.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleCommand.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleCommand.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleEncounter.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleEncounter.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleFaction.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleFaction.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleFlowState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleFlowState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleOutcome.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleOutcome.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattlePlayerInput.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattlePlayerInput.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleResult.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleResult.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleReturnPlan.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleReturnPlan.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleSnapshot.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleSnapshot.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleStartRequest.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/BattleStartRequest.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IBattleConfigProvider.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IBattleConfigProvider.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IEnemyBattlePolicy.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IEnemyBattlePolicy.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IExplorationBattleBridge.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Battle/IExplorationBattleBridge.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/IRunRandomSource.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/IRunRandomSource.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/RandomComponent.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/RandomComponent.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/RunRandomSource.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Random/RunRandomSource.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/RunBattleCoordinator.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/RunBattleCoordinator.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/RunPlayerState.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/RunPlayerState.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/TurnBattleComponent.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/Run/TurnBattleComponent.cs.meta create mode 100644 Assets/GameMain/Scripts/Runtime/Debugger/BattleDebuggerWindow.cs create mode 100644 Assets/GameMain/Scripts/Runtime/Debugger/BattleDebuggerWindow.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleActorCardItem.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleActorCardItem.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleEnemySlotItem.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleEnemySlotItem.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleForm.Logic.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleForm.Logic.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleForm.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleForm.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleTurnSlotItem.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleTurnSlotItem.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleView.cs create mode 100644 Assets/GameMain/Scripts/UI/Battle/BattleView.cs.meta create mode 100644 Assets/GameMain/UI/UIForms/BattleForm.prefab create mode 100644 Assets/GameMain/UI/UIForms/BattleForm.prefab.meta create mode 100644 Assets/GameMain/UI/UIItems/BattleEnemySlotItem.prefab create mode 100644 Assets/GameMain/UI/UIItems/BattleEnemySlotItem.prefab.meta create mode 100644 Assets/GameMain/UI/UIItems/BattleTurnSlotItem.prefab create mode 100644 Assets/GameMain/UI/UIItems/BattleTurnSlotItem.prefab.meta create mode 100644 Docs/Tech/04_TurnBasedCombatDevelopmentTODO.md diff --git a/Assets/GameMain/DataTables/tbformattext.bytes b/Assets/GameMain/DataTables/tbformattext.bytes index a63b4d0..39e454a 100644 --- a/Assets/GameMain/DataTables/tbformattext.bytes +++ b/Assets/GameMain/DataTables/tbformattext.bytes @@ -1 +1 @@ - ATKFormatText ATK {0} MATFormatText MAT {0} SPDFormatText SPD {0} HPFormatTextHP {0} MPFormatTextMP {0} STAFormatText STA {0}WarehouseCapacity{0} ITEMS / {1} TOTAL UNITS \ No newline at end of file + ATKFormatText ATK {0} MATFormatText MAT {0} SPDFormatText SPD {0} HPFormatTextHP {0} MPFormatTextMP {0} STAFormatText STA {0}WarehouseCapacity{0} ITEMS / {1} TOTAL UNITS BattleBarText {0} / {1} \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbformattext.json b/Assets/GameMain/DataTables/tbformattext.json index 32ef809..9979391 100644 --- a/Assets/GameMain/DataTables/tbformattext.json +++ b/Assets/GameMain/DataTables/tbformattext.json @@ -26,5 +26,9 @@ { "name": "WarehouseCapacity", "format": "{0} ITEMS / {1} TOTAL UNITS" + }, + { + "name": "BattleBarText", + "format": "{0} / {1}" } ] \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbuiformconfig.bytes b/Assets/GameMain/DataTables/tbuiformconfig.bytes index 628fef0868897b587f3b0872499c6e22e1ff0acc..3cf769ea8b57290489f724d00abafc2eb5bfb5e1 100644 GIT binary patch delta 30 lcma#ZV`QDk7%rC1<&;=bl9TF|UzE%4lA4xSnp48S2mpq_2>Jj3 delta 7 Ocmd0cU}Txd7!CjjIsvr+ diff --git a/Assets/GameMain/DataTables/tbuiformconfig.json b/Assets/GameMain/DataTables/tbuiformconfig.json index 80a5bcd..3a04a1e 100644 --- a/Assets/GameMain/DataTables/tbuiformconfig.json +++ b/Assets/GameMain/DataTables/tbuiformconfig.json @@ -26,5 +26,12 @@ "UIGroupName": "Default", "allowMultiInstance": false, "pauseCoveredUIForm": true + }, + { + "id": 103, + "assetName": "BattleForm", + "UIGroupName": "Default", + "allowMultiInstance": false, + "pauseCoveredUIForm": true } ] \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Base/Gen/UIFormType.cs b/Assets/GameMain/Scripts/Base/Gen/UIFormType.cs index 9eead81..ba9408b 100644 --- a/Assets/GameMain/Scripts/Base/Gen/UIFormType.cs +++ b/Assets/GameMain/Scripts/Base/Gen/UIFormType.cs @@ -36,6 +36,10 @@ namespace SepCore.Definition /// 关于 /// AboutForm = 102, + /// + /// 战斗界面 + /// + BattleForm = 103, } } diff --git a/Assets/GameMain/Scripts/Editor/BattleUIPrefabBuilder.cs b/Assets/GameMain/Scripts/Editor/BattleUIPrefabBuilder.cs new file mode 100644 index 0000000..9fda1c8 --- /dev/null +++ b/Assets/GameMain/Scripts/Editor/BattleUIPrefabBuilder.cs @@ -0,0 +1,592 @@ +using System; +using System.Linq; +using SepCore.UI; +using TMPro; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; + +namespace SepCore.Editor +{ + public static class BattleUIPrefabBuilder + { + private const string PrefabPath = "Assets/GameMain/UI/UIForms/BattleForm.prefab"; + private const string CharacterAtlasPath = "Assets/GameMain/Textures/Characters.png"; + private const string EnemyAtlasPath = "Assets/GameMain/Textures/Monsters.png"; + private const string ItemAtlasPath = "Assets/GameMain/Textures/Items.png"; + private const string PreviewCameraName = "__BattlePreviewCamera"; + private const string PreviewRootName = "__BattlePreview"; + private const string PreviousScenePathKey = "BattleUI.PreviousScenePath"; + + private static readonly Color Overlay = new Color32(8, 7, 12, 184); + private static readonly Color Charcoal = new Color32(25, 24, 30, 255); + private static readonly Color CharcoalSoft = new Color32(42, 39, 47, 255); + private static readonly Color Ivory = new Color32(244, 237, 214, 255); + private static readonly Color Ink = new Color32(43, 37, 35, 255); + private static readonly Color White = new Color32(248, 248, 246, 255); + private static readonly Color MutedWhite = new Color32(192, 190, 184, 255); + private static readonly Color Green = new Color32(62, 164, 94, 255); + private static readonly Color Blue = new Color32(62, 119, 190, 255); + private static readonly Color Red = new Color32(191, 69, 65, 255); + private static readonly Color Gold = new Color32(218, 169, 64, 255); + private static readonly Color EnemyFrame = new Color32(91, 39, 54, 235); + + private static TMP_FontAsset font; + private static Sprite panelBeige; + private static Sprite panelBrown; + private static Sprite panelBlue; + private static Sprite buttonBlue; + private static Sprite buttonBluePressed; + private static Sprite buttonBrown; + private static Sprite buttonBrownPressed; + private static Sprite buttonBeige; + private static Sprite buttonBeigePressed; + private static Sprite buttonGrey; + private static Sprite buttonGreyPressed; + private static Sprite escapeIcon; + private static Sprite[] playerIcons; + private static Sprite[] enemyIcons; + private static Sprite attackIcon; + private static Sprite skillIcon; + private static Sprite itemIcon; + + [MenuItem("Utility/UI/Build Battle UI")] + public static void Build() + { + LoadAssets(); + + Scene previousScene = SceneManager.GetActiveScene(); + Scene temporaryScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive); + GameObject root = null; + + try + { + SceneManager.SetActiveScene(temporaryScene); + root = BuildRoot(); + UISerializationRoot serializationRoot = root.GetComponent(); + serializationRoot.RefreshItems(); + UIAssetsTools.ApplyGeneratedComponentsToRoot(serializationRoot); + + bool success; + PrefabUtility.SaveAsPrefabAsset(root, PrefabPath, out success); + if (!success) + { + throw new InvalidOperationException("Failed to save BattleForm prefab."); + } + } + finally + { + if (root != null) + { + UnityEngine.Object.DestroyImmediate(root); + } + + if (previousScene.IsValid()) + { + SceneManager.SetActiveScene(previousScene); + } + + EditorSceneManager.CloseScene(temporaryScene, true); + } + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Selection.activeObject = AssetDatabase.LoadAssetAtPath(PrefabPath); + Debug.Log("Battle UI prefab rebuilt: " + PrefabPath); + } + + [MenuItem("Utility/UI/Preview Battle UI")] + public static void OpenPreview() + { + ClosePreviewInternal(); + + Scene previousScene = SceneManager.GetActiveScene(); + SessionState.SetString(PreviousScenePathKey, previousScene.path); + Scene previewScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive); + SceneManager.SetActiveScene(previewScene); + + GameObject cameraObject = new GameObject(PreviewCameraName, typeof(Camera)); + Camera previewCamera = cameraObject.GetComponent(); + previewCamera.clearFlags = CameraClearFlags.SolidColor; + previewCamera.backgroundColor = new Color(0.22f, 0.25f, 0.23f, 1f); + previewCamera.orthographic = true; + previewCamera.orthographicSize = 5f; + cameraObject.transform.position = new Vector3(0f, 0f, -10f); + + GameObject prefab = AssetDatabase.LoadAssetAtPath(PrefabPath); + if (prefab == null) + { + throw new InvalidOperationException("Build BattleForm before opening its preview."); + } + + GameObject previewRoot = (GameObject)PrefabUtility.InstantiatePrefab(prefab, previewScene); + previewRoot.name = PreviewRootName; + Canvas canvas = previewRoot.GetComponent(); + canvas.renderMode = RenderMode.ScreenSpaceCamera; + canvas.worldCamera = previewCamera; + canvas.planeDistance = 1f; + previewRoot.GetComponent().localScale = Vector3.one; + Selection.activeGameObject = previewRoot; + Debug.Log("Battle UI preview opened in an unsaved scene."); + } + + [MenuItem("Utility/UI/Close Battle UI Preview")] + public static void ClosePreview() + { + ClosePreviewInternal(); + } + + private static void ClosePreviewInternal() + { + Camera[] cameras = Resources.FindObjectsOfTypeAll(); + Camera previewCamera = cameras.FirstOrDefault(candidate => candidate != null && + candidate.gameObject.name == PreviewCameraName && candidate.gameObject.scene.IsValid()); + if (previewCamera == null) + { + return; + } + + Scene previewScene = previewCamera.gameObject.scene; + string previousPath = SessionState.GetString(PreviousScenePathKey, string.Empty); + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene loadedScene = SceneManager.GetSceneAt(i); + if (loadedScene.IsValid() && loadedScene.path == previousPath) + { + SceneManager.SetActiveScene(loadedScene); + break; + } + } + + EditorSceneManager.CloseScene(previewScene, true); + SessionState.EraseString(PreviousScenePathKey); + } + + private static GameObject BuildRoot() + { + GameObject root = new GameObject("BattleForm", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), + typeof(GraphicRaycaster), typeof(CanvasGroup), typeof(UISerializationRoot)); + root.layer = LayerMask.NameToLayer("UI"); + + Canvas canvas = root.GetComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.pixelPerfect = true; + canvas.overrideSorting = true; + canvas.sortingOrder = 500; + + CanvasScaler scaler = root.GetComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920f, 1080f); + scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; + scaler.matchWidthOrHeight = 0.5f; + + RectTransform rootRect = root.GetComponent(); + rootRect.anchorMin = Vector2.zero; + rootRect.anchorMax = Vector2.one; + rootRect.offsetMin = Vector2.zero; + rootRect.offsetMax = Vector2.zero; + + AddImage(Stretch("BattleOverlay", rootRect, 0f, 0f, 0f, 0f), Overlay, null); + RectTransform safeFrame = Fixed("SafeFrame", rootRect, new Vector2(0.5f, 0.5f), + new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1920f, 1080f)); + + BuildTurnOrder(safeFrame); + BuildPlayers(safeFrame); + BuildEnemies(safeFrame); + BuildActionPanel(safeFrame); + return root; + } + + private static void BuildTurnOrder(RectTransform parent) + { + RectTransform panel = AddPanel( + Fixed("TurnOrderPanel", parent, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), + new Vector2(0f, -24f), new Vector2(1016f, 112f)), panelBrown, Color.white); + AddImage(TopStretch("ActiveAccent", panel, 0f, 0f, 0f, 6f), Gold, null); + + TextMeshProUGUI round = AddText( + Fixed("RoundText", panel, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), + new Vector2(28f, 0f), new Vector2(156f, 74f)), "ROUND 01", 25, Ivory, + TextAlignmentOptions.MidlineLeft, FontStyles.Bold); + RegisterReference(round, "roundText"); + + Sprite[] icons = + { + playerIcons[3], enemyIcons[2], playerIcons[0], enemyIcons[0], + playerIcons[2], enemyIcons[1], playerIcons[1], enemyIcons[3] + }; + string[] labels = { "P4", "E3", "P1", "E1", "P3", "E2", "P2", "E4" }; + for (int i = 0; i < 8; i++) + { + RectTransform slotRect = Fixed("TurnSlot" + (i + 1), panel, new Vector2(0f, 0.5f), + new Vector2(0f, 0.5f), new Vector2(194f + i * 98f, 0f), new Vector2(82f, 82f)); + Image background = AddImage(slotRect, i == 0 ? Ivory : CharcoalSoft, null); + background.raycastTarget = false; + + RectTransform marker = Stretch("ActiveMarker", slotRect, -3f, -3f, -3f, -3f); + Image markerImage = AddImage(marker, Color.clear, null); + Outline markerOutline = marker.gameObject.AddComponent(); + markerOutline.effectColor = Gold; + markerOutline.effectDistance = new Vector2(3f, -3f); + markerOutline.useGraphicAlpha = false; + marker.gameObject.SetActive(i == 0); + + Image icon = AddSprite(Fixed("Icon", slotRect, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), + new Vector2(0f, -5f), new Vector2(54f, 54f)), icons[i], Color.white, true); + TextMeshProUGUI label = AddText( + Fixed("Label", slotRect, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), + new Vector2(0f, 5f), new Vector2(68f, 22f)), labels[i], 15, + i == 0 ? Ink : MutedWhite, TextAlignmentOptions.Center, FontStyles.Bold); + + BattleTurnSlotItem slot = slotRect.gameObject.AddComponent(); + slot.ConfigureEditor(marker.gameObject, icon, label); + RegisterReference(slot, "turnSlot" + (i + 1)); + } + } + + private static void BuildPlayers(RectTransform parent) + { + int[] hp = { 92, 150, 58, 100 }; + int[] maxHp = { 120, 150, 90, 100 }; + int[] mp = { 24, 10, 72, 50 }; + int[] maxMp = { 40, 30, 80, 50 }; + + CreatePlayerCard(parent, 0, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(32f, -32f), false, + hp[0], maxHp[0], mp[0], maxMp[0], false); + CreatePlayerCard(parent, 1, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-32f, -32f), true, + hp[1], maxHp[1], mp[1], maxMp[1], false); + CreatePlayerCard(parent, 2, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(32f, 28f), false, + hp[2], maxHp[2], mp[2], maxMp[2], false); + CreatePlayerCard(parent, 3, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-32f, 28f), true, + hp[3], maxHp[3], mp[3], maxMp[3], true); + } + + private static void CreatePlayerCard(RectTransform parent, int index, Vector2 anchor, Vector2 pivot, + Vector2 position, bool mirror, int hp, int maxHp, int mp, int maxMp, bool active) + { + RectTransform cardRect = AddPanel(Fixed("PlayerCard" + (index + 1), parent, anchor, pivot, position, + new Vector2(400f, 176f)), panelBeige, Color.white); + + RectTransform marker = TopStretch("ActiveMarker", cardRect, 0f, 0f, 0f, 7f); + AddImage(marker, Gold, null); + marker.gameObject.SetActive(active); + + float iconX = mirror ? 278f : 18f; + float contentX = mirror ? 18f : 140f; + RectTransform iconFrame = Fixed("IconFrame", cardRect, new Vector2(0f, 1f), new Vector2(0f, 1f), + new Vector2(iconX, -18f), new Vector2(104f, 140f)); + AddImage(iconFrame, Charcoal, null); + Image icon = AddSprite(Fixed("Icon", iconFrame, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), + new Vector2(0f, -8f), new Vector2(88f, 96f)), playerIcons[index], Color.white, true); + AddText(Fixed("PartyIndex", iconFrame, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), + new Vector2(0f, 7f), new Vector2(84f, 24f)), "P" + (index + 1), 14, MutedWhite, + TextAlignmentOptions.Center, FontStyles.Bold); + + TextMeshProUGUI name = AddText( + Fixed("CharacterName", cardRect, new Vector2(0f, 1f), new Vector2(0f, 1f), + new Vector2(contentX, -14f), new Vector2(242f, 38f)), "角色 " + (index + 1), 24, Ink, + mirror ? TextAlignmentOptions.TopRight : TextAlignmentOptions.TopLeft, FontStyles.Bold); + Image hpFill = CreateBar(cardRect, "HP", contentX, 64f, 242f, Green, hp, maxHp, out TextMeshProUGUI hpText); + Image mpFill = CreateBar(cardRect, "MP", contentX, 112f, 242f, Blue, mp, maxMp, out TextMeshProUGUI mpText); + + BattleActorCardItem card = cardRect.gameObject.AddComponent(); + card.ConfigureEditor(marker.gameObject, icon, hpFill, mpFill, name, hpText, mpText); + RegisterReference(card, "playerCard" + (index + 1)); + } + + private static void BuildEnemies(RectTransform parent) + { + RectTransform enemyRoot = Fixed("EnemyRoot", parent, new Vector2(0.5f, 0.5f), + new Vector2(0.5f, 0.5f), new Vector2(0f, 32f), new Vector2(820f, 290f)); + int[] hp = { 36, 80, 44, 61 }; + int[] maxHp = { 50, 80, 60, 80 }; + + for (int i = 0; i < 4; i++) + { + RectTransform slotRect = Fixed("EnemySlot" + (i + 1), enemyRoot, new Vector2(0.5f, 0.5f), + new Vector2(0.5f, 0.5f), new Vector2(-300f + i * 200f, 0f), new Vector2(170f, 248f)); + Image targetGraphic = AddImage(slotRect, new Color(EnemyFrame.r, EnemyFrame.g, EnemyFrame.b, 0.68f), null); + targetGraphic.raycastTarget = true; + Button targetButton = slotRect.gameObject.AddComponent