commit 42ad134759690dd47748cf15fe5d4a01101ba5b0 Author: huangjun Date: Sun Aug 30 07:48:19 2026 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bb4898 --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# This .gitignore file should be placed at the root of your Unity project. +# Based on: https://github.com/github/gitignore/blob/main/Unity.gitignore + +# Unity-generated directories +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ +/[Mm]emoryCaptures/ +/[Rr]ecordings/ + +# Asset Store and generated editor plugins +/[Aa]ssets/AssetStoreTools* +/[Aa]ssets/Plugins/Editor/JetBrains* + +# IDE and editor state +.idea/ +.vscode/ +.vs/ +.gradle/ +.consulo/ +ExportedObj/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db +*.DotSettings.user + +# Unity-generated metadata for debugging databases +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Crash and diagnostic output +sysinfo.txt +mono_crash.* +crash_*.dmp + +# Build artifacts +*.apk +*.aab +*.unitypackage +*.app +crashlytics-build.properties + +# Addressables-generated data +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* + +# Temporary scene backups +/[Aa]ssets/[Ss]cenes/*.unity.orig + +# OS-generated files +.DS_Store +Thumbs.db + +# Source assets that are intentionally kept outside version control +/Assets/RawResources/ +/Assets/RawResources.meta + +# Luban local configuration and temporary workbook files +/数据表/path.txt +/数据表/Datas/~$*.xlsx +/数据表/Datas/.~*.xlsx +/数据表/Datas/*.xlsx~ +/数据表/**/*.log +/数据表/.cache/ +/数据表/Temp/ + +# Luban-generated outputs. Sources, schemas, templates, tools, and scripts stay tracked. +/数据表/output/ +/数据表/output-server/ +/Assets/Scripts/Gen/ +/Assets/Scripts/Gen.meta diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 0000000..d414656 --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9582c68991bdb4bd5ab4977edc2d675b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/SbeUiPrefabBuilder.cs b/Assets/Editor/SbeUiPrefabBuilder.cs new file mode 100644 index 0000000..ddcee7e --- /dev/null +++ b/Assets/Editor/SbeUiPrefabBuilder.cs @@ -0,0 +1,866 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using UI; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; + +internal static class SbeUiPrefabBuilder +{ + private const string RawUiPath = "Assets/RawResources/kenney_ui-pack-rpg-expansion/PNG"; + private const string UiSpritePath = "Assets/GameResources/UI/Sprites"; + private const string PrefabPath = "Assets/Prefab/UI"; + private const string TerrainPath = "Assets/GameResources/Texture/Terrian.png"; + + private static readonly Color Backdrop = new Color32(17, 24, 32, 255); + private static readonly Color BackdropRaised = new Color32(24, 34, 44, 255); + private static readonly Color BackdropSoft = new Color32(34, 48, 58, 255); + private static readonly Color Ink = new Color32(46, 42, 37, 255); + private static readonly Color MutedInk = new Color32(111, 101, 90, 255); + private static readonly Color Paper = new Color32(242, 231, 201, 255); + private static readonly Color PaperMuted = new Color32(214, 194, 142, 255); + private static readonly Color White = new Color32(245, 247, 248, 255); + private static readonly Color MutedWhite = new Color32(170, 182, 190, 255); + private static readonly Color Blue = new Color32(52, 127, 168, 255); + private static readonly Color Green = new Color32(93, 155, 97, 255); + private static readonly Color Red = new Color32(185, 87, 79, 255); + private static readonly Color Gold = new Color32(214, 169, 61, 255); + + private static Font uiFont; + private static Sprite panelBrown; + private static Sprite panelBeige; + private static Sprite panelBeigeLight; + private static Sprite panelBlue; + private static Sprite panelInsetBrown; + private static Sprite panelInsetLight; + private static Sprite buttonBlue; + private static Sprite buttonBluePressed; + private static Sprite buttonBrown; + private static Sprite buttonBrownPressed; + private static Sprite buttonGrey; + private static Sprite buttonGreyPressed; + private static Sprite buttonBeige; + private static Sprite buttonBeigePressed; + private static Sprite buttonSquareGrey; + private static Sprite buttonSquareGreyPressed; + private static Sprite buttonSquareBlue; + private static Sprite buttonSquareBluePressed; + private static Sprite iconCheck; + private static Sprite iconClose; + private static Sprite iconBack; + private static Dictionary terrainSprites; + + [MenuItem("SBE/UI/Build UI Prefabs")] + public static void BuildAll() + { + uiFont = Resources.GetBuiltinResource("LegacyRuntime.ttf"); + if (uiFont == null) + { + throw new InvalidOperationException("Unity built-in UI font is unavailable."); + } + + EnsureFolder(UiSpritePath); + EnsureFolder(PrefabPath); + PrepareSprites(); + + BuildPrefab("LobbyUI", BuildLobby); + BuildPrefab("MainWarehouseUI", BuildWarehouse); + BuildPrefab("RunBackpackUI", BuildRunBackpack); + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Selection.activeObject = AssetDatabase.LoadAssetAtPath(PrefabPath + "/LobbyUI.prefab"); + Debug.Log("SBE UI prefabs rebuilt: LobbyUI, MainWarehouseUI, RunBackpackUI."); + } + + private static void PrepareSprites() + { + panelBrown = PrepareSprite("panel_brown.png", new Vector4(12f, 12f, 12f, 12f)); + panelBeige = PrepareSprite("panel_beige.png", new Vector4(12f, 12f, 12f, 12f)); + panelBeigeLight = PrepareSprite("panel_beigeLight.png", new Vector4(12f, 12f, 12f, 12f)); + panelBlue = PrepareSprite("panel_blue.png", new Vector4(12f, 12f, 12f, 12f)); + panelInsetBrown = PrepareSprite("panelInset_brown.png", new Vector4(11f, 11f, 11f, 11f)); + panelInsetLight = PrepareSprite("panelInset_beigeLight.png", new Vector4(11f, 11f, 11f, 11f)); + buttonBlue = PrepareSprite("buttonLong_blue.png", new Vector4(15f, 12f, 15f, 12f)); + buttonBluePressed = PrepareSprite("buttonLong_blue_pressed.png", new Vector4(15f, 12f, 15f, 12f)); + buttonBrown = PrepareSprite("buttonLong_brown.png", new Vector4(15f, 12f, 15f, 12f)); + buttonBrownPressed = PrepareSprite("buttonLong_brown_pressed.png", new Vector4(15f, 12f, 15f, 12f)); + buttonGrey = PrepareSprite("buttonLong_grey.png", new Vector4(15f, 12f, 15f, 12f)); + buttonGreyPressed = PrepareSprite("buttonLong_grey_pressed.png", new Vector4(15f, 12f, 15f, 12f)); + buttonBeige = PrepareSprite("buttonLong_beige.png", new Vector4(15f, 12f, 15f, 12f)); + buttonBeigePressed = PrepareSprite("buttonLong_beige_pressed.png", new Vector4(15f, 12f, 15f, 12f)); + buttonSquareGrey = PrepareSprite("buttonSquare_grey.png", new Vector4(10f, 10f, 10f, 10f)); + buttonSquareGreyPressed = PrepareSprite("buttonSquare_grey_pressed.png", new Vector4(10f, 10f, 10f, 10f)); + buttonSquareBlue = PrepareSprite("buttonSquare_blue.png", new Vector4(10f, 10f, 10f, 10f)); + buttonSquareBluePressed = PrepareSprite("buttonSquare_blue_pressed.png", new Vector4(10f, 10f, 10f, 10f)); + iconCheck = PrepareSprite("iconCheck_blue.png", Vector4.zero); + iconClose = PrepareSprite("iconCross_grey.png", Vector4.zero); + iconBack = PrepareSprite("arrowSilver_left.png", Vector4.zero); + + terrainSprites = AssetDatabase.LoadAllAssetsAtPath(TerrainPath) + .OfType() + .Where(sprite => sprite.name.StartsWith("Terrian_", StringComparison.Ordinal)) + .ToDictionary(sprite => ParseSuffix(sprite.name), sprite => sprite); + } + + private static void BuildPrefab(string name, Func factory) + { + Scene previousScene = SceneManager.GetActiveScene(); + Scene temporaryScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive); + GameObject root = null; + + try + { + SceneManager.SetActiveScene(temporaryScene); + root = factory(); + root.GetComponent().RefreshItems(); + string path = PrefabPath + "/" + name + ".prefab"; + bool success; + PrefabUtility.SaveAsPrefabAsset(root, path, out success); + if (!success) + { + throw new InvalidOperationException("Failed to save prefab at " + path + "."); + } + } + finally + { + if (root != null) + { + UnityEngine.Object.DestroyImmediate(root); + } + + if (previousScene.IsValid()) + { + SceneManager.SetActiveScene(previousScene); + } + + EditorSceneManager.CloseScene(temporaryScene, true); + } + } + + private static GameObject BuildLobby() + { + GameObject root = CreateCanvas("LobbyUI", 0); + RectTransform rootRect = root.GetComponent(); + AddImage(Stretch("Backdrop", rootRect, 0f, 0f, 0f, 0f), Backdrop, null); + RectTransform safeFrame = Fixed("SafeFrame", rootRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1920f, 1080f)); + AddShell(safeFrame, "HOME"); + + AddText(Fixed("ScreenTitle", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(344f, -144f), new Vector2(850f, 58f)), + "PREPARE THE RUN", 34, White, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("ScreenSubtitle", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(344f, -194f), new Vector2(880f, 34f)), + "Squad, threat tier and deterministic run seed", 17, MutedWhite, TextAnchor.MiddleLeft, FontStyle.Normal); + + RectTransform squadPanel = Panel(Fixed("SquadPanel", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(336f, -248f), new Vector2(960f, 690f)), panelBrown, Color.white); + AddSectionHeading(squadPanel, "SQUAD", "4 / 4 SELECTED", Green); + + int[] portraits = { 84, 85, 86, 87 }; + int[] hp = { 120, 150, 90, 100 }; + int[] mp = { 40, 30, 80, 50 }; + int[] speed = { 12, 8, 10, 16 }; + for (int i = 0; i < 4; i++) + { + float x = 34f + (i % 2) * 448f; + float y = 102f + (i / 2) * 270f; + CreateCharacterCard(squadPanel, i + 1, x, y, hp[i], mp[i], speed[i], Terrain(portraits[i])); + } + + RectTransform deploymentPanel = Panel(Fixed("DeploymentPanel", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(1328f, -248f), new Vector2(528f, 690f)), panelBeige, Color.white); + AddText(Fixed("Title", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(34f, -28f), new Vector2(460f, 42f)), + "DEPLOYMENT", 25, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("DifficultyLabel", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(34f, -98f), new Vector2(460f, 26f)), + "THREAT TIER", 15, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + CreateSegmentedToggles(deploymentPanel, "DifficultyTabs", new[] { "TIER I", "TIER II", "TIER III" }, 34f, 134f, 460f, 62f, 0); + + AddText(Fixed("SeedLabel", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(34f, -230f), new Vector2(460f, 26f)), + "RUN SEED", 15, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + InputField seedInput = CreateInputField(deploymentPanel, "SeedInput", "240829", "ENTER SEED", 34f, 266f, 302f, 64f, true); + RegisterReference(seedInput, "seedInput"); + Button randomizeButton = CreateButton(Fixed("RandomizeButton", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(350f, -266f), new Vector2(144f, 64f)), + "RANDOMIZE", ButtonKind.Brown, 15); + RegisterReference(randomizeButton, "randomizeButton"); + + RectTransform runSummary = Panel(Fixed("RunSummary", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(34f, -362f), new Vector2(460f, 174f)), panelInsetLight, Color.white); + CreateStatLine(runSummary, "PartySize", "PARTY", "4", 24f, 24f, Green); + CreateStatLine(runSummary, "MapCount", "MAP", "01", 24f, 72f, Blue); + CreateStatLine(runSummary, "TimeLimit", "TIME LIMIT", "25:00", 24f, 120f, Red); + + Button beginRunButton = CreateButton(Fixed("BeginRunButton", deploymentPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(34f, -574f), new Vector2(460f, 82f)), + "BEGIN RUN", ButtonKind.Blue, 24); + RegisterReference(beginRunButton, "beginRunButton"); + + RectTransform footer = Panel(Fixed("FooterStatus", safeFrame, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(336f, 38f), new Vector2(1520f, 62f)), panelInsetBrown, Color.white); + AddText(Fixed("Storage", footer, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(360f, 40f)), + "MAIN WAREHOUSE 0 ITEMS", 16, Paper, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("SeedStatus", footer, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(440f, 40f)), + "READY FOR DEPLOYMENT", 15, Green, TextAnchor.MiddleRight, FontStyle.Bold); + return root; + } + + private static GameObject BuildWarehouse() + { + GameObject root = CreateCanvas("MainWarehouseUI", 10); + RectTransform rootRect = root.GetComponent(); + AddImage(Stretch("Backdrop", rootRect, 0f, 0f, 0f, 0f), Backdrop, null); + RectTransform safeFrame = Fixed("SafeFrame", rootRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1920f, 1080f)); + AddShell(safeFrame, "WAREHOUSE"); + + AddText(Fixed("ScreenTitle", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(344f, -144f), new Vector2(620f, 58f)), + "MAIN WAREHOUSE", 34, White, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Capacity", safeFrame, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-64f, -158f), new Vector2(360f, 38f)), + "UNLIMITED STORAGE", 16, MutedWhite, TextAnchor.MiddleRight, FontStyle.Bold); + + RectTransform inventoryPanel = Panel(Fixed("InventoryPanel", safeFrame, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(336f, -220f), new Vector2(1080f, 800f)), panelBrown, Color.white); + CreateSegmentedToggles(inventoryPanel, "CategoryTabs", new[] { "ALL", "LOOT", "EQUIPMENT", "CONSUMABLES" }, 30f, 26f, 650f, 58f, 0); + InputField searchInput = CreateInputField(inventoryPanel, "SearchInput", string.Empty, "SEARCH ITEMS", 706f, 26f, 260f, 58f, false); + RegisterReference(searchInput, "searchInput"); + Button sortButton = CreateButton(Fixed("SortButton", inventoryPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(980f, -26f), new Vector2(70f, 58f)), + "A-Z", ButtonKind.Grey, 15); + RegisterReference(sortButton, "sortButton"); + + RectTransform scrollRoot = Panel(Fixed("ItemScroll", inventoryPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -112f), new Vector2(1020f, 620f)), panelInsetBrown, Color.white); + ScrollRect scroll = scrollRoot.gameObject.AddComponent(); + RegisterReference(scroll, "itemScroll"); + RectTransform viewport = Stretch("Viewport", scrollRoot, 20f, 24f, 22f, 20f); + Image viewportImage = AddImage(viewport, new Color(1f, 1f, 1f, 0.001f), null); + viewportImage.raycastTarget = true; + viewport.gameObject.AddComponent(); + + RectTransform content = TopStretch("Content", viewport, 0f, 12f, 0f, 610f); + content.pivot = new Vector2(0.5f, 1f); + GridLayoutGroup grid = content.gameObject.AddComponent(); + grid.cellSize = new Vector2(104f, 104f); + grid.spacing = new Vector2(16f, 16f); + grid.padding = new RectOffset(8, 8, 8, 8); + grid.startCorner = GridLayoutGroup.Corner.UpperLeft; + grid.startAxis = GridLayoutGroup.Axis.Horizontal; + grid.childAlignment = TextAnchor.UpperLeft; + grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount; + grid.constraintCount = 8; + + int[] icons = { 101, 102, 117, 123, 124, 104, 130, 118, 129, 115 }; + Color[] rarities = { White, Green, Blue, Gold, Red, White, Blue, Green, Gold, White }; + string[] quantities = { "28", "7", "3", "1", "1", "1", "1", "1", "1", "4" }; + for (int i = 0; i < 40; i++) + { + bool populated = i < icons.Length; + CreateItemSlot(content, "Slot_" + (i + 1).ToString("00", CultureInfo.InvariantCulture), + populated ? Terrain(icons[i]) : null, + populated ? rarities[i] : MutedInk, + populated ? quantities[i] : string.Empty, + populated && i == 5); + } + + scroll.viewport = viewport; + scroll.content = content; + scroll.horizontal = false; + scroll.vertical = true; + scroll.movementType = ScrollRect.MovementType.Clamped; + scroll.scrollSensitivity = 30f; + scroll.verticalNormalizedPosition = 1f; + + AddText(Fixed("ItemCount", inventoryPanel, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(34f, 25f), new Vector2(400f, 34f)), + "10 ITEMS / 48 TOTAL UNITS", 15, PaperMuted, TextAnchor.MiddleLeft, FontStyle.Bold); + + RectTransform details = Panel(Fixed("ItemDetailsPanel", safeFrame, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-64f, -220f), new Vector2(408f, 800f)), panelBeige, Color.white); + AddText(Fixed("Heading", details, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -26f), new Vector2(348f, 40f)), + "SELECTED ITEM", 20, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + RectTransform iconPanel = Panel(Fixed("IconPanel", details, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -92f), new Vector2(184f, 184f)), panelInsetLight, Color.white); + AddSprite(Fixed("Icon", iconPanel, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(112f, 112f)), Terrain(104), Color.white, true); + AddText(Fixed("Name", details, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -300f), new Vector2(348f, 42f)), + "TRAINING SWORD", 24, Ink, TextAnchor.MiddleCenter, FontStyle.Bold); + AddText(Fixed("Type", details, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -344f), new Vector2(348f, 30f)), + "WEAPON / COMMON", 15, MutedInk, TextAnchor.MiddleCenter, FontStyle.Bold); + + RectTransform stats = Panel(Fixed("Stats", details, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -404f), new Vector2(352f, 176f)), panelInsetLight, Color.white); + CreateStatLine(stats, "Attack", "ATK", "+5", 22f, 22f, Red); + CreateStatLine(stats, "Stack", "STACK LIMIT", "1", 22f, 72f, Blue); + CreateStatLine(stats, "ItemId", "ITEM ID", "5101", 22f, 122f, MutedInk); + + AddText(Fixed("Description", details, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(32f, -608f), new Vector2(344f, 70f)), + "A simple weapon prepared for the first expedition.", 16, MutedInk, TextAnchor.UpperLeft, FontStyle.Normal); + Button openLoadoutButton = CreateButton(Fixed("OpenLoadoutButton", details, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(30f, 30f), new Vector2(348f, 68f)), + "OPEN LOADOUT", ButtonKind.Blue, 19); + RegisterReference(openLoadoutButton, "openLoadoutButton"); + return root; + } + + private static GameObject BuildRunBackpack() + { + GameObject root = CreateCanvas("RunBackpackUI", 100); + RectTransform rootRect = root.GetComponent(); + AddImage(Stretch("WorldScrim", rootRect, 0f, 0f, 0f, 0f), new Color(0.035f, 0.055f, 0.07f, 0.88f), null); + + RectTransform window = Panel(Fixed("BackpackWindow", rootRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1700f, 910f)), panelBrown, Color.white); + RectTransform topBar = Panel(TopStretch("TopBar", window, 24f, 24f, 22f, 86f), panelInsetBrown, Color.white); + AddText(Fixed("Title", topBar, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(26f, 0f), new Vector2(420f, 54f)), + "RUN BACKPACK", 28, Paper, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Seed", topBar, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(90f, 0f), new Vector2(330f, 46f)), + "SEED 240829", 16, PaperMuted, TextAnchor.MiddleCenter, FontStyle.Bold); + AddText(Fixed("Timer", topBar, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-106f, 0f), new Vector2(190f, 54f)), + "18:42", 30, Gold, TextAnchor.MiddleRight, FontStyle.Bold); + Button closeButton = CreateIconButton(Fixed("CloseButton", topBar, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-18f, 0f), new Vector2(58f, 58f)), iconClose, false); + RegisterReference(closeButton, "closeButton"); + + RectTransform backpackPanel = Panel(Fixed("BackpackPanel", window, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -132f), new Vector2(1080f, 748f)), panelBeige, Color.white); + AddText(Fixed("Heading", backpackPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -24f), new Vector2(420f, 40f)), + "SHARED BACKPACK", 23, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Capacity", backpackPanel, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-30f, -26f), new Vector2(250f, 38f)), + "12 / 30", 20, Blue, TextAnchor.MiddleRight, FontStyle.Bold); + + RectTransform bagGrid = Fixed("BackpackGrid", backpackPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -84f), new Vector2(1020f, 510f)); + GridLayoutGroup bagLayout = bagGrid.gameObject.AddComponent(); + bagLayout.cellSize = new Vector2(150f, 94f); + bagLayout.spacing = new Vector2(18f, 10f); + bagLayout.startCorner = GridLayoutGroup.Corner.UpperLeft; + bagLayout.startAxis = GridLayoutGroup.Axis.Horizontal; + bagLayout.childAlignment = TextAnchor.UpperLeft; + bagLayout.constraint = GridLayoutGroup.Constraint.FixedColumnCount; + bagLayout.constraintCount = 6; + + int[] bagIcons = { 101, 101, 102, 117, 123, 124, 104, 130, 118, 129, 115, 126 }; + string[] bagCounts = { "12", "16", "7", "3", "1", "1", "1", "1", "1", "1", "4", "2" }; + Color[] bagRarity = { White, White, Green, Blue, Gold, Red, White, Green, Blue, Gold, White, Green }; + for (int i = 0; i < 30; i++) + { + bool populated = i < bagIcons.Length; + CreateItemSlot(bagGrid, "Slot_" + (i + 1).ToString("00", CultureInfo.InvariantCulture), + populated ? Terrain(bagIcons[i]) : null, + populated ? bagRarity[i] : MutedInk, + populated ? bagCounts[i] : string.Empty, + i == 0); + } + + AddText(Fixed("SafeHeading", backpackPanel, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(30f, 124f), new Vector2(360f, 34f)), + "SAFE CASE", 19, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("SafeCapacity", backpackPanel, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(300f, 124f), new Vector2(130f, 34f)), + "3 / 5", 17, Green, TextAnchor.MiddleLeft, FontStyle.Bold); + RectTransform safeGrid = Fixed("SafeGrid", backpackPanel, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(30f, 24f), new Vector2(830f, 90f)); + GridLayoutGroup safeLayout = safeGrid.gameObject.AddComponent(); + safeLayout.cellSize = new Vector2(150f, 90f); + safeLayout.spacing = new Vector2(18f, 0f); + safeLayout.constraint = GridLayoutGroup.Constraint.FixedColumnCount; + safeLayout.constraintCount = 5; + for (int i = 0; i < 5; i++) + { + CreateItemSlot(safeGrid, "SafeSlot_" + (i + 1).ToString("00", CultureInfo.InvariantCulture), + i < 3 ? Terrain(bagIcons[i]) : null, + i < 3 ? bagRarity[i] : MutedInk, + i < 3 ? bagCounts[i] : string.Empty, + false); + } + + RectTransform characterPanel = Panel(Fixed("CharacterPanel", window, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -132f), new Vector2(548f, 748f)), panelBeige, Color.white); + AddText(Fixed("Heading", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -24f), new Vector2(320f, 38f)), + "CHARACTER LOADOUT", 20, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + CreateCharacterSelector(characterPanel); + + RectTransform portrait = Panel(Fixed("PortraitPanel", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -128f), new Vector2(142f, 142f)), panelBlue, Color.white); + AddSprite(Fixed("Portrait", portrait, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(104f, 104f)), Terrain(84), Color.white, true); + AddText(Fixed("CharacterName", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(194f, -138f), new Vector2(322f, 38f)), + "CHARACTER 01", 22, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("CharacterId", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(194f, -178f), new Vector2(322f, 26f)), + "ID 1001", 14, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Hp", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(194f, -220f), new Vector2(150f, 28f)), + "HP 120 / 120", 15, Red, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Mp", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(358f, -220f), new Vector2(150f, 28f)), + "MP 40 / 40", 15, Blue, TextAnchor.MiddleLeft, FontStyle.Bold); + + RectTransform stats = Panel(Fixed("Stats", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -294f), new Vector2(488f, 110f)), panelInsetLight, Color.white); + CreateStatLine(stats, "Attack", "ATK", "14", 22f, 14f, Red, 210f); + CreateStatLine(stats, "Magic", "MAT", "6", 262f, 14f, Blue, 204f); + CreateStatLine(stats, "Speed", "SPEED", "12", 22f, 60f, Green, 210f); + CreateStatLine(stats, "Skill", "SKILL", "01", 262f, 60f, Gold, 204f); + + AddText(Fixed("EquipmentHeading", characterPanel, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -430f), new Vector2(488f, 34f)), + "EQUIPMENT", 18, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + CreateEquipmentSlot(characterPanel, "WeaponSlot", "WEAPON", "TRAINING SWORD", Terrain(104), 30f, 470f, Red); + CreateEquipmentSlot(characterPanel, "ArmorSlot", "Armor", "EMPTY", null, 282f, 470f, Blue); + + RectTransform selectedItem = Panel(Fixed("SelectedItem", characterPanel, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(30f, 28f), new Vector2(488f, 142f)), panelInsetLight, Color.white); + AddSprite(Fixed("Icon", selectedItem, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(58f, 0f), new Vector2(76f, 76f)), Terrain(101), Color.white, true); + AddText(Fixed("Name", selectedItem, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(112f, -22f), new Vector2(196f, 32f)), + "OLD COIN x12", 18, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Rarity", selectedItem, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(112f, -58f), new Vector2(196f, 24f)), + "COMMON LOOT", 13, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + Button moveToSafeButton = CreateButton(Fixed("MoveToSafeButton", selectedItem, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-18f, 0f), new Vector2(150f, 64f)), + "TO SAFE", ButtonKind.Blue, 17); + RegisterReference(moveToSafeButton, "moveToSafeButton"); + return root; + } + + private static GameObject CreateCanvas(string name, int sortingOrder) + { + GameObject root = new GameObject(name, typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster), typeof(CanvasGroup)); + root.layer = LayerMask.NameToLayer("UI"); + root.AddComponent(); + Canvas canvas = root.GetComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.pixelPerfect = true; + canvas.overrideSorting = true; + canvas.sortingOrder = sortingOrder; + + CanvasScaler scaler = root.GetComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920f, 1080f); + scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; + scaler.matchWidthOrHeight = 1f; + + RectTransform rect = root.GetComponent(); + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + rect.offsetMin = Vector2.zero; + rect.offsetMax = Vector2.zero; + return root; + } + + private static void AddShell(RectTransform root, string activeSection) + { + RectTransform header = Stretch("Header", root, 0f, 0f, 972f, 0f); + AddImage(header, BackdropRaised, null); + AddImage(Fixed("Accent", header, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(288f, 6f)), Blue, null); + AddText(Fixed("Brand", header, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(62f, 4f), new Vector2(150f, 78f)), + "SBE", 50, White, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Product", header, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(182f, -4f), new Vector2(360f, 46f)), + "EXPEDITION DESK", 16, MutedWhite, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Profile", header, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-64f, 0f), new Vector2(360f, 46f)), + "LOCAL PROFILE", 15, MutedWhite, TextAnchor.MiddleRight, FontStyle.Bold); + + RectTransform sidebar = Stretch("Sidebar", root, 0f, 1632f, 0f, 108f); + AddImage(sidebar, BackdropRaised, null); + AddText(Fixed("NavigationLabel", sidebar, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(40f, -44f), new Vector2(220f, 28f)), + "NAVIGATION", 13, MutedWhite, TextAnchor.MiddleLeft, FontStyle.Bold); + CreateNavigationButton(sidebar, "HomeButton", "HOME", 98f, activeSection == "HOME", "homeButton"); + CreateNavigationButton(sidebar, "WarehouseButton", "WAREHOUSE", 172f, activeSection == "WAREHOUSE", "warehouseButton"); + CreateNavigationButton(sidebar, "LoadoutButton", "LOADOUT", 246f, activeSection == "LOADOUT", "loadoutButton"); + + RectTransform status = Panel(Fixed("ProfileStatus", sidebar, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(28f, 34f), new Vector2(232f, 126f)), panelInsetBrown, Color.white); + AddText(Fixed("SaveLabel", status, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(18f, -16f), new Vector2(196f, 24f)), + "LOCAL SAVE", 13, PaperMuted, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("SaveState", status, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(18f, -48f), new Vector2(196f, 30f)), + "READY", 18, Green, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Version", status, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(18f, 12f), new Vector2(196f, 22f)), + "PROTOTYPE 01", 12, MutedWhite, TextAnchor.MiddleLeft, FontStyle.Bold); + } + + private static void CreateNavigationButton(RectTransform parent, string name, string label, float top, bool selected, string variableName) + { + RectTransform rect = Fixed(name, parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -top), new Vector2(232f, 58f)); + Button button = CreateButton(rect, label, selected ? ButtonKind.Blue : ButtonKind.Grey, 17); + RegisterReference(button, variableName); + if (selected) + { + AddImage(Fixed("SelectionMarker", rect, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(-8f, 0f), new Vector2(5f, 34f)), Gold, null); + } + } + + private static void CreateCharacterCard(RectTransform parent, int index, float x, float y, int hp, int mp, int speed, Sprite portraitSprite) + { + RectTransform card = Panel(Fixed("CharacterSlot_" + index.ToString("00", CultureInfo.InvariantCulture), parent, + new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, -y), new Vector2(414f, 236f)), panelBeige, Color.white); + Toggle toggle = card.gameObject.AddComponent(); + toggle.targetGraphic = card.GetComponent(); + toggle.targetGraphic.raycastTarget = true; + + RectTransform portrait = Panel(Fixed("PortraitPanel", card, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(132f, 180f)), panelBlue, Color.white); + AddSprite(Fixed("Portrait", portrait, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(104f, 104f)), portraitSprite, Color.white, true); + AddText(Fixed("Name", card, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(178f, -24f), new Vector2(190f, 34f)), + "CHARACTER " + index.ToString("00", CultureInfo.InvariantCulture), 19, Ink, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Id", card, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(178f, -60f), new Vector2(150f, 24f)), + "ID " + (1000 + index).ToString(CultureInfo.InvariantCulture), 12, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Hp", card, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(178f, -104f), new Vector2(190f, 28f)), + "HP " + hp.ToString(CultureInfo.InvariantCulture), 15, Red, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Mp", card, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(178f, -138f), new Vector2(190f, 28f)), + "MP " + mp.ToString(CultureInfo.InvariantCulture), 15, Blue, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("Speed", card, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(178f, -172f), new Vector2(190f, 28f)), + "SPEED " + speed.ToString(CultureInfo.InvariantCulture), 15, Green, TextAnchor.MiddleLeft, FontStyle.Bold); + + RectTransform check = Fixed("Selected", card, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -24f), new Vector2(32f, 32f)); + Image checkImage = AddImage(check, Color.white, iconCheck); + checkImage.preserveAspect = true; + toggle.graphic = checkImage; + toggle.isOn = true; + } + + private static void CreateCharacterSelector(RectTransform parent) + { + RectTransform groupRoot = Fixed("CharacterTabs", parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -74f), new Vector2(488f, 52f)); + ToggleGroup group = groupRoot.gameObject.AddComponent(); + group.allowSwitchOff = false; + HorizontalLayoutGroup layout = groupRoot.gameObject.AddComponent(); + layout.spacing = 10f; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = true; + + for (int i = 0; i < 4; i++) + { + RectTransform tab = NewRect("CharacterTab_" + (i + 1).ToString("00", CultureInfo.InvariantCulture), groupRoot); + Image baseImage = AddImage(tab, Color.white, buttonGrey); + baseImage.type = Image.Type.Sliced; + baseImage.color = BackdropSoft; + baseImage.raycastTarget = true; + Toggle toggle = tab.gameObject.AddComponent(); + toggle.targetGraphic = baseImage; + toggle.group = group; + RectTransform selected = Stretch("Selected", tab, 0f, 0f, 0f, 0f); + Image selectedImage = AddImage(selected, Color.white, buttonBlue); + selectedImage.type = Image.Type.Sliced; + toggle.graphic = selectedImage; + AddText(Stretch("Label", tab, 0f, 0f, 0f, 0f), (i + 1).ToString("00", CultureInfo.InvariantCulture), 16, White, TextAnchor.MiddleCenter, FontStyle.Bold); + toggle.isOn = i == 0; + } + } + + private static void CreateEquipmentSlot(RectTransform parent, string name, string heading, string itemName, Sprite icon, float x, float y, Color accent) + { + RectTransform slot = Panel(Fixed(name, parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, -y), new Vector2(236f, 108f)), panelInsetLight, Color.white); + AddImage(Fixed("Accent", slot, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(4f, 0f), new Vector2(5f, 72f)), accent, null); + if (icon != null) + { + AddSprite(Fixed("Icon", slot, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(44f, 0f), new Vector2(56f, 56f)), icon, Color.white, true); + } + AddText(Fixed("Heading", slot, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(82f, -20f), new Vector2(140f, 24f)), + heading.ToUpperInvariant(), 12, MutedInk, TextAnchor.MiddleLeft, FontStyle.Bold); + AddText(Fixed("ItemName", slot, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(82f, 18f), new Vector2(140f, 42f)), + itemName, 14, Ink, TextAnchor.MiddleLeft, FontStyle.Bold, true, 11); + Button button = slot.gameObject.AddComponent