diff --git a/README.md b/README.md index c97cc65..80fa45e 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,15 @@ git pull ``` slaymaple/ ├── Global/ # 월드 전역 설정 · 공용 모델 · 게임로직 -│ ├── common.gamelogic # SlayCardCatalog / SlayRunState / SlayCombatManager 부착 지점 +│ ├── common.gamelogic # SlayDeckController 부착 지점 (카드 UI 전투) │ ├── Player.model # 플레이어 모델 │ ├── *.model # 몬스터 등 공용 모델 │ ├── WorldConfig.config # 월드 설정 │ └── ... ├── RootDesk/ │ └── MyDesk/ # 작업용 책상 — codeblock(스크립트)·모델·타일셋 -│ ├── Monster.codeblock +│ ├── SlayDeckController.codeblock # 카드 UI 전투 컨트롤러 (생성물) +│ ├── Monster.codeblock # 필드 액션 몬스터 (HP·피격·리스폰, 카드 전투와 별개) │ ├── MonsterAttack.codeblock │ ├── PlayerAttack.codeblock │ ├── PlayerHit.codeblock @@ -58,7 +59,11 @@ slaymaple/ │ ├── UIToast.codeblock │ └── RectTileData_Henesys.tileset ├── map/ -│ └── map01.map # 메인 맵 +│ ├── map01.map ~ map11.map # 맵 11종 (공식 배경 + STS풍 우측 배치) +├── tools/ # 결정적 생성기 (단일 소스) +│ ├── gen-slaydeck.mjs # 카드/덱 UI · SlayDeckController · common 생성 +│ ├── gen-cardhand.mjs # 손패 카드 엔티티 생성 +│ └── gen-maps.mjs # 맵 생성 ├── ui/ # UI 그룹 (Default / Popup / Toast) ├── docs/ │ └── slaymaple_basic_framework.md # 전투 프레임워크 설계 문서 @@ -71,42 +76,61 @@ slaymaple/ ## 게임 프레임워크 현황 -`Global/common.gamelogic`의 `/common` 엔티티에 부착된 세 컴포넌트가 전투의 핵심입니다. +현재 전투는 `Global/common.gamelogic`의 `/common` 엔티티에 부착된 **`SlayDeckController` 단일 컴포넌트**로 동작합니다. 모든 카드/덱/전투 관련 산출물(`ui/DefaultGroup.ui` · `RootDesk/MyDesk/SlayDeckController.codeblock` · `common.gamelogic`)은 **`tools/gen-slaydeck.mjs` 단일 소스에서 생성**됩니다(직접 편집 금지, 결정적 출력). -| 컴포넌트 | 역할 | -|---|---| -| `SlayCardCatalog` | 카드 데이터, 시작 덱 구성, 보상 풀, 카드 복제 정의 | -| `SlayRunState` | HP·골드·층수·덱·유물·카드 보상 등 런(run) 영속 데이터 관리 | -| `SlayCombatManager` | 턴 진행, 드로우/버림/소멸 더미, 에너지, 적 의도, 방어도, 데미지, 승패 처리 | +| 컴포넌트 | 상태 | 역할 | +|---|---|---| +| `SlayDeckController` | ✅ 구현됨 | 카드 손패 UI 전투 — 드로우/버림/재셔플, 에너지, 카드 효과(데미지/방어), 적 HP·방어·의도, 턴 진행, 승패 | +| `Monster.codeblock` | ✅ 구현됨 | 필드 액션 몬스터(HP·피격·리스폰) — 카드 전투와는 **별개** 시스템 | -### 프로토타입 흐름 -1. `SlayRunState`가 HP 80 · 10장 시작 덱으로 새 런 시작 -2. `SlayCombatManager`가 데모 전투 자동 시작 -3. 매 플레이어 턴: 에너지 3 회복, 방어도 초기화, 적 의도 갱신, 5장 드로우 -4. 카드 사용 시 에너지 소모 → 데미지/방어/드로우/에너지/상태이상 적용 → 버림 또는 소멸 -5. 턴 종료 시 손패 버림, 적 의도 실행, 상태이상 처리, 다음 턴 시작 -6. 전투 승리 시 잔여 HP 저장, 골드 15 지급, 카드 보상 3종 생성 +### 구현된 카드 전투 (단일 전투 루프) +- **카드 손패 UI**: 에너지 3, 매 턴 5장 드로우, 버림 더미·재셔플, 카드 클릭 사용, 종류별 색상. +- **카드 3종**: 타격(피해 6) · 방어(방어도 5) · 강타(피해 10). 각 카드에 `damage`/`block` 수치 필드. 시작 덱 10장. +- **전투 상태**: 플레이어 `HP`/`Block`, 적 `HP`/`Block`/`Intent(의도)`. 적 의도는 **결정적 사이클**(공격10 → 공격6 → 방어8)로 다음 행동을 미리 표시. +- **규칙**: 데미지는 방어도 먼저 차감 후 잔여만 HP에 적용. 플레이어 Block은 턴 시작 시 리셋. +- **턴 흐름**: 카드 사용(`Attack`→적 HP↓, `Skill`→플레이어 Block↑) → 턴 종료 → 적 턴(의도 실행) → 다음 플레이어 턴. +- **승패**: 적 HP 0 → 승리, 플레이어 HP 0 → 패배. 승패 시 입력 잠금 + 결과 표시(전투 보상 훅 자리 = E 예정). +- **UI(CombatHud)**: 적 패널(이름·HP·방어·의도)·플레이어 패널(HP·방어)·승패 결과 텍스트. + +> ⚠️ 플레이어 HP(80)·적 HP(45)·의도 수치(10·6·8)는 **루프 검증용 임시 placeholder**입니다. +> 향후 캐릭터 특성별/몬스터별 데이터로 분리할 예정입니다(아래 D 참조). ### 유용한 스크립트 호출 -`/common` 엔티티에 붙은 스크립트에서: +`/common` 엔티티(또는 Play Test 컨텍스트)에서: ```lua -self.Entity.SlayCombatManager:PlayCard(1, 1) -self.Entity.SlayCombatManager:EndPlayerTurn() -self.Entity.SlayCombatManager:DebugPlayFirstPlayable() -self.Entity.SlayRunState:PickReward(1) -self.Entity.SlayCombatManager:StartCombat("elite") +local c = _EntityService:GetEntityByPath("/common").SlayDeckController +c:PlayCard(1) -- 손패 slot 카드 사용 +c:EndPlayerTurn() -- 턴 종료 → 적 턴 → 다음 턴 +c:StartCombat() -- 전투 재시작(상태 초기화) ``` 상세 설계는 [`docs/slaymaple_basic_framework.md`](docs/slaymaple_basic_framework.md) 참조. --- +## 향후 설계 (미구현 — 목표 아키텍처) + +아래는 로그라이크 메타까지 확장했을 때의 **목표 컴포넌트 구조**로, 현재는 미구현입니다. (현재는 `SlayDeckController` 하나가 카드 전투만 담당) + +| 컴포넌트 (계획) | 역할 | +|---|---| +| `SlayCardCatalog` | 카드 데이터, 시작 덱 구성, 보상 풀, 카드 복제 정의 | +| `SlayRunState` | HP·골드·층수·덱·유물·카드 보상 등 런(run) 영속 데이터 관리 | +| `SlayCombatManager` | 턴 진행, 드로우/버림/소멸 더미, 에너지, 적 의도, 방어도, 데미지, 승패 처리 | + +> 위 구조로 가더라도 카드/적 데이터는 `tools/`의 결정적 생성기를 단일 소스로 유지하는 방향을 권장합니다. + +--- + ## 다음 구현 단계 -- [ ] HP·방어도·에너지·적 의도·손패 5버튼을 렌더링하는 전투 UI -- [ ] 전투/엘리트/상점/휴식/이벤트/보스 노드를 가진 맵 노드 UI -- [ ] `OnCombatStart` / `OnCardPlayed` / `OnTurnStart` / `OnCombatReward` 훅을 가진 유물 시스템 -- [ ] 적 행동 패턴을 데이터로 정의 (현재 단순 의도 패턴 → 무브셋) -- [ ] 런 루프 완성 후 저장/불러오기 +- [x] HP·방어도·에너지·적 의도·손패 카드를 렌더링하는 전투 UI **(완료 — `SlayDeckController` + CombatHud)** +- [x] 카드 사용이 실제 데미지/방어/적 의도/승패에 반영되는 단일 전투 루프 **(완료)** +- [ ] 카드/적 데이터를 `data/cards.json` · `data/enemies.json`로 외부화 (D) +- [ ] 전투를 N회 자동 시뮬레이션하는 밸런스 검증 도구 `tools/sim-balance.mjs` (F, D 선행) +- [ ] 전투/엘리트/상점/휴식/이벤트/보스 노드를 가진 맵 노드 UI (E) +- [ ] `OnCombatStart` / `OnCardPlayed` / `OnTurnStart` / `OnCombatReward` 훅을 가진 유물 시스템 (E) +- [ ] 적 행동 패턴을 데이터로 정의 (현재 단순 결정적 의도 사이클 → 무브셋) +- [ ] 런 영속(HP/골드/층/덱/유물) + 저장/불러오기 (E, 루프 end-to-end 후) --- diff --git a/RootDesk/MyDesk/SlayDeckController.codeblock b/RootDesk/MyDesk/SlayDeckController.codeblock index 62a893d..008b241 100644 --- a/RootDesk/MyDesk/SlayDeckController.codeblock +++ b/RootDesk/MyDesk/SlayDeckController.codeblock @@ -91,6 +91,209 @@ "SyncDirection": 0, "Attributes": [], "Name": "NewGameHandler" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "Cards" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "PlayerHp" + }, + { + "Type": "number", + "DefaultValue": "80", + "SyncDirection": 0, + "Attributes": [], + "Name": "PlayerMaxHp" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "PlayerBlock" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyHp" + }, + { + "Type": "number", + "DefaultValue": "45", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyMaxHp" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyBlock" + }, + { + "Type": "number", + "DefaultValue": "1", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyIntentIndex" + }, + { + "Type": "boolean", + "DefaultValue": "false", + "SyncDirection": 0, + "Attributes": [], + "Name": "CombatOver" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyIntents" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "EnemyName" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "RunDeck" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "Gold" + }, + { + "Type": "number", + "DefaultValue": "0", + "SyncDirection": 0, + "Attributes": [], + "Name": "Floor" + }, + { + "Type": "number", + "DefaultValue": "3", + "SyncDirection": 0, + "Attributes": [], + "Name": "RunLength" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "RewardChoices" + }, + { + "Type": "boolean", + "DefaultValue": "false", + "SyncDirection": 0, + "Attributes": [], + "Name": "RunActive" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "Enemies" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "MapNodes" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "MapStart" + }, + { + "Type": "string", + "DefaultValue": "\"\"", + "SyncDirection": 0, + "Attributes": [], + "Name": "CurrentNodeId" + }, + { + "Type": "string", + "DefaultValue": "\"\"", + "SyncDirection": 0, + "Attributes": [], + "Name": "CurrentEnemyId" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "ShopChoices" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "ShopBought" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "Relics" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "RunRelics" + }, + { + "Type": "any", + "DefaultValue": "nil", + "SyncDirection": 0, + "Attributes": [], + "Name": "RelicPool" + }, + { + "Type": "string", + "DefaultValue": "\"\"", + "SyncDirection": 0, + "Attributes": [], + "Name": "ShopRelic" + }, + { + "Type": "boolean", + "DefaultValue": "false", + "SyncDirection": 0, + "Attributes": [], + "Name": "ShopRelicBought" } ], "Methods": [ @@ -118,7 +321,7 @@ "Name": null }, "Arguments": [], - "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/MainMenu\", true)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CardHand\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/DeckHud\", false)\nself:BindMenuButtons()", + "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/MainMenu\", true)\nself:BindMenuButtons()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -133,7 +336,7 @@ "Name": null }, "Arguments": [], - "Code": "local buttonEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MainMenu/NewGameButton\")\nif buttonEntity == nil or buttonEntity.ButtonComponent == nil then\n\treturn\nend\nif self.NewGameHandler ~= nil then\n\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler)\n\tself.NewGameHandler = nil\nend\nself.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, self.StartNewGame)", + "Code": "local buttonEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MainMenu/NewGameButton\")\nif buttonEntity == nil or buttonEntity.ButtonComponent == nil then\n\treturn\nend\nif self.NewGameHandler ~= nil then\n\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler)\n\tself.NewGameHandler = nil\nend\nself.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, function() self:StartNewGame() end)", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -148,12 +351,42 @@ "Name": null }, "Arguments": [], - "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/MainMenu\", false)\nself:SetEntityEnabled(\"/ui/DefaultGroup/CardHand\", true)\nself:SetEntityEnabled(\"/ui/DefaultGroup/DeckHud\", true)\nself:ConfigureTurnBasedMonsters()\nself:ConfigureTurnBasedPlayer()\nself:StartCombat()", + "Code": "self:SetEntityEnabled(\"/ui/DefaultGroup/MainMenu\", false)\nself:StartRun()", "Scope": 2, "ExecSpace": 6, "Attributes": [], "Name": "StartNewGame" }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "path" + }, + { + "Type": "boolean", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "enabled" + } + ], + "Code": "local e = _EntityService:GetEntityByPath(path)\nif e ~= nil then\n\te.Enable = enabled\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "SetEntityEnabled" + }, { "Return": { "Type": "void", @@ -163,65 +396,27 @@ "Name": null }, "Arguments": [], - "Code": "self.MaxEnergy = 3\nself.Turn = 0\nself.DiscardPile = {}\nself.Hand = {}\nself.DrawPile = { \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Defend\", \"Defend\", \"Defend\", \"Defend\", \"Bash\" }\nself:Shuffle(self.DrawPile)\nself:BindButtons()\nself:StartPlayerTurn()", + "Code": "self.PlayerMaxHp = 80\nself.PlayerHp = self.PlayerMaxHp\nself.Gold = 0\nself.Floor = 1\nself.RunLength = 3\nself.RunDeck = { \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Defend\", \"Defend\", \"Defend\", \"Defend\", \"Bash\" }\nself.RunActive = true\nself.RunRelics = {}\nself.Relics = {\n\tironHeart = { name = \"강철 심장\", desc = \"전투 시작 시 방어도 +6\", hook = \"combatStart\", effect = \"block\", value = 6 },\n\tenergyCore = { name = \"에너지 코어\", desc = \"턴 시작 시 에너지 +1\", hook = \"turnStart\", effect = \"energy\", value = 1 },\n\tvampire = { name = \"흡혈 송곳니\", desc = \"공격 카드 사용 시 HP +1\", hook = \"cardPlayed\", effect = \"healOnAttack\", value = 1 },\n\tgoldIdol = { name = \"황금 우상\", desc = \"전투 승리 시 골드 +10\", hook = \"combatReward\", effect = \"gold\", value = 10 },\n}\nself.RelicPool = { \"energyCore\", \"vampire\", \"goldIdol\" }\nself.Enemies = {\n\tslime = { name = \"슬라임\", maxHp = 45, intents = { { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 6 }, { kind = \"Defend\", value = 8 } } },\n\tslime_elite = { name = \"정예 슬라임\", maxHp = 70, intents = { { kind = \"Attack\", value = 14 }, { kind = \"Attack\", value = 8 }, { kind = \"Defend\", value = 10 } } },\n\tslime_boss = { name = \"슬라임 킹\", maxHp = 120, intents = { { kind = \"Attack\", value = 18 }, { kind = \"Defend\", value = 12 }, { kind = \"Attack\", value = 10 }, { kind = \"Attack\", value = 22 } } },\n}\nself.MapNodes = {\n\tA = { type = \"combat\", enemy = \"slime\", row = 1, col = -1, next = { \"C\", \"D\" } },\n\tB = { type = \"combat\", enemy = \"slime\", row = 1, col = 1, next = { \"C\", \"D\" } },\n\tC = { type = \"rest\", row = 2, col = -1, next = { \"E\", \"F\" } },\n\tD = { type = \"shop\", row = 2, col = 1, next = { \"E\", \"F\" } },\n\tE = { type = \"elite\", enemy = \"slime_elite\", row = 3, col = -1, next = { \"BOSS\" } },\n\tF = { type = \"combat\", enemy = \"slime\", row = 3, col = 1, next = { \"BOSS\" } },\n\tBOSS = { type = \"boss\", enemy = \"slime_boss\", row = 4, col = 0, next = { } },\n}\nself.MapStart = { \"A\", \"B\" }\nself.CurrentNodeId = \"\"\nself.CurrentEnemyId = \"\"\nself:BindButtons()\nself:AddRelic(\"ironHeart\")\nself:ShowMap()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "StartRun" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self.MaxEnergy = 3\nself.Turn = 0\nlocal enemy = self.Enemies[self.CurrentEnemyId]\nlocal mult = 1 + (self.Floor - 1) * 0.6\nself.PlayerBlock = 0\nself.EnemyName = enemy.name\nself.EnemyMaxHp = math.floor(enemy.maxHp * mult)\nself.EnemyHp = self.EnemyMaxHp\nself.EnemyBlock = 0\nself.EnemyIntents = {}\nfor i = 1, #enemy.intents do\n\tself.EnemyIntents[i] = { kind = enemy.intents[i].kind, value = math.floor(enemy.intents[i].value * mult) }\nend\nself.EnemyIntentIndex = 1\nself.CombatOver = false\nself.DiscardPile = {}\nself.Hand = {}\nself.Cards = {\n\tStrike = { name = \"타격\", cost = 1, desc = \"피해 6\", kind = \"Attack\", damage = 6 },\n\tDefend = { name = \"방어\", cost = 1, desc = \"방어도 5\", kind = \"Skill\", block = 5 },\n\tBash = { name = \"강타\", cost = 2, desc = \"피해 10\", kind = \"Attack\", damage = 10 },\n}\nself.DrawPile = {}\nfor i = 1, #self.RunDeck do\n\tself.DrawPile[i] = self.RunDeck[i]\nend\nself:Shuffle(self.DrawPile)\nself:RenderCombat()\nself:StartPlayerTurn()\nself:ApplyRelics(\"combatStart\")\nself:RenderCombat()", "Scope": 2, "ExecSpace": 6, "Attributes": [], "Name": "StartCombat" }, - { - "Return": { - "Type": "void", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": null - }, - "Arguments": [], - "Code": "for mapIndex = 1, 11 do\n\tlocal mapName = \"map\" .. string.format(\"%02d\", mapIndex)\n\tfor i = 1, 6 do\n\t\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath(\"/maps/\" .. mapName .. \"/Monster\" .. tostring(i)))\n\tend\n\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath(\"/maps/\" .. mapName .. \"/StaticMonsterTemplate\"))\n\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath(\"/maps/\" .. mapName .. \"/MoveMonsterTemplate\"))\n\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath(\"/maps/\" .. mapName .. \"/ChaseMonsterTemplate\"))\n\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath(\"/maps/\" .. mapName .. \"/monster-43\"))\nend", - "Scope": 2, - "ExecSpace": 6, - "Attributes": [], - "Name": "ConfigureTurnBasedMonsters" - }, - { - "Return": { - "Type": "void", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": null - }, - "Arguments": [ - { - "Type": "Entity", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": "monster" - } - ], - "Code": "if monster == nil then\n\treturn\nend\nif monster.AIWanderComponent ~= nil then\n\tmonster.AIWanderComponent.Enable = false\nend\nif monster.AIChaseComponent ~= nil then\n\tmonster.AIChaseComponent.Enable = false\nend\nif monster.MovementComponent ~= nil then\n\tmonster.MovementComponent.Enable = false\nend\nif monster.RigidbodyComponent ~= nil then\n\tmonster.RigidbodyComponent.MoveVelocity = Vector2.zero\n\tmonster.RigidbodyComponent.RealMoveVelocity = Vector2.zero\nend\nif monster.TransformComponent ~= nil then\n\tlocal scale = monster.TransformComponent.Scale\n\tmonster.TransformComponent.Scale = Vector3(math.abs(scale.x), math.abs(scale.y), scale.z)\nend\nif monster.StateAnimationComponent ~= nil and monster.SpriteRendererComponent ~= nil then\n\tlocal stand = monster.StateAnimationComponent.ActionSheet[\"stand\"]\n\tif stand ~= nil and stand ~= \"\" then\n\t\tmonster.SpriteRendererComponent.SpriteRUID = stand\n\tend\nend", - "Scope": 2, - "ExecSpace": 6, - "Attributes": [], - "Name": "ConfigureMonsterForTurnCombat" - }, - { - "Return": { - "Type": "void", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": null - }, - "Arguments": [], - "Code": "local player = nil\npcall(function()\n\tif _UserService ~= nil and _UserService.LocalPlayer ~= nil then\n\t\tplayer = _UserService.LocalPlayer\n\tend\nend)\npcall(function()\n\tif player == nil and _UserService ~= nil and _UserService.LocalPlayerEntity ~= nil then\n\t\tplayer = _UserService.LocalPlayerEntity\n\tend\nend)\npcall(function()\n\tif player == nil and _UserService ~= nil and _UserService.GetLocalPlayer ~= nil then\n\t\tplayer = _UserService:GetLocalPlayer()\n\tend\nend)\nif player ~= nil and player.Entity ~= nil then\n\tplayer = player.Entity\nend\nif player == nil then\n\treturn\nend\nif player.PlayerControllerComponent ~= nil then\n\tplayer.PlayerControllerComponent.Enable = false\n\tpcall(function() player.PlayerControllerComponent.LookDirectionX = 1 end)\nend\nif player.MovementComponent ~= nil then\n\tplayer.MovementComponent.Enable = false\n\tpcall(function() player.MovementComponent.InputSpeed = 0 end)\n\tpcall(function() player.MovementComponent.JumpForce = 0 end)\nend\nif player.RigidbodyComponent ~= nil then\n\tplayer.RigidbodyComponent.MoveVelocity = Vector2.zero\n\tplayer.RigidbodyComponent.RealMoveVelocity = Vector2.zero\nend\nif player.TransformComponent ~= nil then\n\tlocal scale = player.TransformComponent.Scale\n\tplayer.TransformComponent.Scale = Vector3(math.abs(scale.x), math.abs(scale.y), scale.z)\nend", - "Scope": 2, - "ExecSpace": 6, - "Attributes": [], - "Name": "ConfigureTurnBasedPlayer" - }, { "Return": { "Type": "void", @@ -254,7 +449,7 @@ "Name": null }, "Arguments": [], - "Code": "local buttonEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/EndTurnButton\")\nif buttonEntity == nil or buttonEntity.ButtonComponent == nil then\n\treturn\nend\nif self.EndTurnHandler ~= nil then\n\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)\n\tself.EndTurnHandler = nil\nend\nself.EndTurnHandler = buttonEntity:ConnectEvent(ButtonClickEvent, self.EndPlayerTurn)", + "Code": "local endTurn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/DeckHud/EndTurnButton\")\nif endTurn ~= nil and endTurn.ButtonComponent ~= nil then\n\tif self.EndTurnHandler ~= nil then\n\t\tendTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)\n\t\tself.EndTurnHandler = nil\n\tend\n\tself.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)\nend\nfor i = 1, 5 do\n\tlocal cardEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(i))\n\tif cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then\n\t\tcardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal rc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Reward\" .. tostring(i))\n\tif rc ~= nil and rc.ButtonComponent ~= nil then\n\t\trc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end)\n\tend\nend\nlocal skip = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud/Skip\")\nif skip ~= nil and skip.ButtonComponent ~= nil then\n\tskip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end)\nend\nlocal mapNodeIds = { \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"BOSS\" }\nfor i = 1, #mapNodeIds do\n\tlocal nid = mapNodeIds[i]\n\tlocal mn = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud/Node_\" .. nid)\n\tif mn ~= nil and mn.ButtonComponent ~= nil then\n\t\tmn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end)\n\tend\nend\nfor i = 1, 3 do\n\tlocal sc = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Card\" .. tostring(i))\n\tif sc ~= nil and sc.ButtonComponent ~= nil then\n\t\tsc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end)\n\tend\nend\nlocal shopLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Leave\")\nif shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then\n\tshopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend\nlocal shopRelic = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Relic\")\nif shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then\n\tshopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end)\nend\nlocal restLeave = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RestHud/Leave\")\nif restLeave ~= nil and restLeave.ButtonComponent ~= nil then\n\trestLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end)\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -269,7 +464,7 @@ "Name": null }, "Arguments": [], - "Code": "self.Turn = self.Turn + 1\nself.Energy = self.MaxEnergy\nself:DrawCards(5)\nself:RenderHand(true)", + "Code": "self.Turn = self.Turn + 1\nself.Energy = self.MaxEnergy\nself:ApplyRelics(\"turnStart\")\nself.PlayerBlock = 0\nself:DrawCards(5)\nself:RenderHand(true)\nself:RenderCombat()", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -284,7 +479,7 @@ "Name": null }, "Arguments": [], - "Code": "for i = 1, #self.Hand do\n\ttable.insert(self.DiscardPile, self.Hand[i])\nend\nself.Hand = {}\nself:RenderHand(false)\nself:RenderPiles()\n_TimerService:SetTimerOnce(function() self:StartPlayerTurn() end, 0.45)", + "Code": "if self.CombatOver == true then\n\treturn\nend\nfor i = 1, #self.Hand do\n\ttable.insert(self.DiscardPile, self.Hand[i])\nend\nself.Hand = {}\nself:RenderHand(false)\nself:RenderPiles()\nself:EnemyTurn()\nself:CheckCombatEnd()\nif self.CombatOver == true then\n\treturn\nend\n_TimerService:SetTimerOnce(function() self:StartPlayerTurn() end, 0.45)", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -337,7 +532,7 @@ "Name": null }, "Arguments": [], - "Code": "self:SetText(\"/ui/DefaultGroup/DeckHud/DrawPile/Count\", tostring(#self.DrawPile))\nself:SetText(\"/ui/DefaultGroup/DeckHud/DiscardPile/Count\", tostring(#self.DiscardPile))\nself:SetText(\"/ui/DefaultGroup/DeckHud/Energy\", \"에너지 \" .. tostring(self.Energy) .. \"/\" .. tostring(self.MaxEnergy))", + "Code": "self:SetText(\"/ui/DefaultGroup/DeckHud/DrawPile/Count\", tostring(#self.DrawPile))\nself:SetText(\"/ui/DefaultGroup/DeckHud/DiscardPile/Count\", tostring(#self.DiscardPile))\nself:SetText(\"/ui/DefaultGroup/DeckHud/Energy\", \"에너지 \" .. string.format(\"%d\", self.Energy) .. \"/\" .. string.format(\"%d\", self.MaxEnergy))", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -390,7 +585,7 @@ "Name": "cardId" } ], - "Code": "local name = cardId\nlocal cost = 0\nlocal desc = \"\"\nlocal kind = \"Skill\"\nif cardId == \"Strike\" then\n\tname = \"타격\"\n\tcost = 1\n\tdesc = \"피해 6\"\n\tkind = \"Attack\"\nelseif cardId == \"Defend\" then\n\tname = \"방어\"\n\tcost = 1\n\tdesc = \"방어도 5\"\n\tkind = \"Skill\"\nelseif cardId == \"Bash\" then\n\tname = \"강타\"\n\tcost = 2\n\tdesc = \"피해 10\"\n\tkind = \"Attack\"\nend\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Cost\", tostring(cost))\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Name\", name)\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Desc\", desc)\nlocal cardEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot))\nif cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then\n\tlocal ok = false\n\tlocal color = nil\n\tif kind == \"Attack\" then\n\t\tok, color = pcall(function() return Color(0.86, 0.42, 0.38, 1) end)\n\telseif kind == \"Skill\" then\n\t\tok, color = pcall(function() return Color(0.42, 0.55, 0.85, 1) end)\n\tend\n\tif ok == true and color ~= nil then\n\t\tcardEntity.SpriteGUIRendererComponent.Color = color\n\tend\nend", + "Code": "local c = self.Cards[cardId]\nif c == nil then\n\tc = { name = cardId, cost = 0, desc = \"\", kind = \"Skill\" }\nend\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Cost\", tostring(c.cost))\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Name\", c.name)\nself:SetText(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot) .. \"/Desc\", c.desc)\nlocal cardEntity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CardHand/Card\" .. tostring(slot))\nif cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then\n\tif c.kind == \"Attack\" then\n\t\tcardEntity.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)\n\telseif c.kind == \"Skill\" then\n\t\tcardEntity.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)\n\telse\n\t\tcardEntity.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)\n\tend\nend", "Scope": 2, "ExecSpace": 6, "Attributes": [], @@ -426,36 +621,6 @@ "Attributes": [], "Name": "SetText" }, - { - "Return": { - "Type": "void", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": null - }, - "Arguments": [ - { - "Type": "string", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": "path" - }, - { - "Type": "boolean", - "DefaultValue": null, - "SyncDirection": 0, - "Attributes": [], - "Name": "enabled" - } - ], - "Code": "local entity = _EntityService:GetEntityByPath(path)\nif entity ~= nil then\n\tentity.Enable = enabled\nend", - "Scope": 2, - "ExecSpace": 6, - "Attributes": [], - "Name": "SetEntityEnabled" - }, { "Return": { "Type": "void", @@ -499,6 +664,484 @@ "ExecSpace": 6, "Attributes": [], "Name": "AnimateCardFrom" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "slot" + } + ], + "Code": "if self.CombatOver == true then\n\treturn\nend\nif self.Hand == nil then\n\treturn\nend\nlocal cardId = self.Hand[slot]\nif cardId == nil then\n\treturn\nend\nlocal c = self.Cards[cardId]\nif c == nil then\n\treturn\nend\nif self.Energy < c.cost then\n\tself:Toast(\"에너지가 부족합니다\")\n\treturn\nend\nself.Energy = self.Energy - c.cost\nif c.kind == \"Attack\" then\n\tif c.damage ~= nil then\n\t\tself:DealDamageToEnemy(c.damage)\n\tend\n\tself:ApplyRelics(\"cardPlayed\")\nelseif c.kind == \"Skill\" then\n\tif c.block ~= nil then\n\t\tself.PlayerBlock = self.PlayerBlock + c.block\n\tend\nend\ntable.remove(self.Hand, slot)\ntable.insert(self.DiscardPile, cardId)\nself:RenderHand(false)\nself:RenderPiles()\nself:RenderCombat()\nself:CheckCombatEnd()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "PlayCard" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "message" + } + ], + "Code": "log(message)", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "Toast" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "amount" + } + ], + "Code": "local dmg = amount\nif self.EnemyBlock > 0 then\n\tlocal absorbed = math.min(self.EnemyBlock, dmg)\n\tself.EnemyBlock = self.EnemyBlock - absorbed\n\tdmg = dmg - absorbed\nend\nself.EnemyHp = self.EnemyHp - dmg\nif self.EnemyHp < 0 then\n\tself.EnemyHp = 0\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "DealDamageToEnemy" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "amount" + } + ], + "Code": "local dmg = amount\nif self.PlayerBlock > 0 then\n\tlocal absorbed = math.min(self.PlayerBlock, dmg)\n\tself.PlayerBlock = self.PlayerBlock - absorbed\n\tdmg = dmg - absorbed\nend\nself.PlayerHp = self.PlayerHp - dmg\nif self.PlayerHp < 0 then\n\tself.PlayerHp = 0\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "DealDamageToPlayer" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self.EnemyBlock = 0\nlocal intent = self.EnemyIntents[self.EnemyIntentIndex]\nif intent ~= nil then\n\tif intent.kind == \"Attack\" then\n\t\tself:DealDamageToPlayer(intent.value)\n\telseif intent.kind == \"Defend\" then\n\t\tself.EnemyBlock = self.EnemyBlock + intent.value\n\tend\nend\nself.EnemyIntentIndex = self.EnemyIntentIndex + 1\nif self.EnemyIntentIndex > #self.EnemyIntents then\n\tself.EnemyIntentIndex = 1\nend\nself:RenderCombat()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "EnemyTurn" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "if self.EnemyHp <= 0 then\n\tself.CombatOver = true\n\tself.Gold = self.Gold + 15\n\tself:ApplyRelics(\"combatReward\")\n\tself:RenderRun()\n\tlocal node = self.MapNodes[self.CurrentNodeId]\n\tif node ~= nil and node.type == \"elite\" then\n\t\tself:AddRelic(self.RelicPool[math.random(1, #self.RelicPool)])\n\tend\n\tif node ~= nil and node.type == \"boss\" then\n\t\tif self.Floor < self.RunLength then\n\t\t\tself.Floor = self.Floor + 1\n\t\t\tself.CurrentNodeId = \"\"\n\t\t\tself.CurrentEnemyId = \"\"\n\t\t\tself:RenderRun()\n\t\t\tself:ShowMap()\n\t\telse\n\t\t\tself:ShowResult(\"런 클리어!\")\n\t\t\tself.RunActive = false\n\t\tend\n\telse\n\t\tself:OfferReward()\n\tend\nelseif self.PlayerHp <= 0 then\n\tself.CombatOver = true\n\tself:ShowResult(\"패배...\")\n\tself.RunActive = false\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "CheckCombatEnd" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "text" + } + ], + "Code": "self:SetText(\"/ui/DefaultGroup/CombatHud/Result\", text)\nlocal entity = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/CombatHud/Result\")\nif entity ~= nil then\n\tentity.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowResult" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self:SetText(\"/ui/DefaultGroup/CombatHud/EnemyName\", self.EnemyName)\nself:SetText(\"/ui/DefaultGroup/CombatHud/EnemyHp\", \"HP \" .. string.format(\"%d\", self.EnemyHp) .. \"/\" .. string.format(\"%d\", self.EnemyMaxHp))\nself:SetText(\"/ui/DefaultGroup/CombatHud/EnemyBlock\", \"방어 \" .. string.format(\"%d\", self.EnemyBlock))\nlocal intent = self.EnemyIntents[self.EnemyIntentIndex]\nlocal intentText = \"\"\nif intent ~= nil then\n\tif intent.kind == \"Attack\" then\n\t\tintentText = \"의도: 공격 \" .. tostring(intent.value)\n\telseif intent.kind == \"Defend\" then\n\t\tintentText = \"의도: 방어 \" .. tostring(intent.value)\n\tend\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/EnemyIntent\", intentText)\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerHp\", \"HP \" .. string.format(\"%d\", self.PlayerHp) .. \"/\" .. string.format(\"%d\", self.PlayerMaxHp))\nself:SetText(\"/ui/DefaultGroup/CombatHud/PlayerBlock\", \"방어 \" .. string.format(\"%d\", self.PlayerBlock))\nself:RenderRun()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "RenderCombat" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self:SetText(\"/ui/DefaultGroup/CombatHud/Floor\", \"막 \" .. string.format(\"%d\", self.Floor) .. \"/\" .. string.format(\"%d\", self.RunLength))\nself:SetText(\"/ui/DefaultGroup/CombatHud/Gold\", \"골드 \" .. string.format(\"%d\", self.Gold))", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "RenderRun" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local pool = {}\nfor id, _ in pairs(self.Cards) do\n\ttable.insert(pool, id)\nend\nself.RewardChoices = {}\nfor i = 1, 3 do\n\tself.RewardChoices[i] = pool[math.random(1, #pool)]\n\tself:ApplyRewardVisual(i, self.RewardChoices[i])\nend\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud\")\nif hud ~= nil then\n\thud.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "OfferReward" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "slot" + }, + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "cardId" + } + ], + "Code": "local c = self.Cards[cardId]\nif c == nil then\n\treturn\nend\nlocal base = \"/ui/DefaultGroup/RewardHud/Reward\" .. tostring(slot)\nself:SetText(base .. \"/Name\", c.name)\nself:SetText(base .. \"/Cost\", tostring(c.cost))\nself:SetText(base .. \"/Desc\", c.desc)\nlocal e = _EntityService:GetEntityByPath(base)\nif e ~= nil and e.SpriteGUIRendererComponent ~= nil then\n\tif c.kind == \"Attack\" then\n\t\te.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)\n\telseif c.kind == \"Skill\" then\n\t\te.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)\n\telse\n\t\te.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)\n\tend\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ApplyRewardVisual" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "slot" + } + ], + "Code": "if self.CombatOver ~= true or self.RunActive ~= true then\n\treturn\nend\nif slot ~= 0 and self.RewardChoices ~= nil then\n\tlocal id = self.RewardChoices[slot]\n\tif id ~= nil then\n\t\ttable.insert(self.RunDeck, id)\n\tend\nend\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RewardHud\")\nif hud ~= nil then\n\thud.Enable = false\nend\nself:ShowMap()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "PickReward" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "hook" + } + ], + "Code": "if self.RunRelics == nil then\n\treturn\nend\nfor i = 1, #self.RunRelics do\n\tlocal r = self.Relics[self.RunRelics[i]]\n\tif r ~= nil and r.hook == hook then\n\t\tif r.effect == \"block\" then\n\t\t\tself.PlayerBlock = self.PlayerBlock + r.value\n\t\telseif r.effect == \"energy\" then\n\t\t\tself.Energy = self.Energy + r.value\n\t\telseif r.effect == \"healOnAttack\" then\n\t\t\tself.PlayerHp = self.PlayerHp + r.value\n\t\t\tif self.PlayerHp > self.PlayerMaxHp then\n\t\t\t\tself.PlayerHp = self.PlayerMaxHp\n\t\t\tend\n\t\telseif r.effect == \"gold\" then\n\t\t\tself.Gold = self.Gold + r.value\n\t\tend\n\tend\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ApplyRelics" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "id" + } + ], + "Code": "if self.RunRelics == nil then\n\tself.RunRelics = {}\nend\ntable.insert(self.RunRelics, id)\nself:RenderRelics()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "AddRelic" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local names = \"\"\nif self.RunRelics ~= nil then\n\tfor i = 1, #self.RunRelics do\n\t\tlocal r = self.Relics[self.RunRelics[i]]\n\t\tif r ~= nil then\n\t\t\tif names == \"\" then\n\t\t\t\tnames = r.name\n\t\t\telse\n\t\t\t\tnames = names .. \", \" .. r.name\n\t\t\tend\n\t\tend\n\tend\nend\nif names == \"\" then\n\tnames = \"없음\"\nend\nself:SetText(\"/ui/DefaultGroup/CombatHud/Relics\", \"유물: \" .. names)", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "RenderRelics" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self:RenderMap()\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud\")\nif hud ~= nil then\n\thud.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowMap" + }, + { + "Return": { + "Type": "boolean", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "id" + } + ], + "Code": "local list\nif self.CurrentNodeId == \"\" then\n\tlist = self.MapStart\nelse\n\tlocal node = self.MapNodes[self.CurrentNodeId]\n\tif node == nil then\n\t\treturn false\n\tend\n\tlist = node.next\nend\nfor i = 1, #list do\n\tif list[i] == id then\n\t\treturn true\n\tend\nend\nreturn false", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "IsReachable" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "for id, node in pairs(self.MapNodes) do\n\tlocal e = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud/Node_\" .. id)\n\tif e ~= nil then\n\t\tlocal reachable = self:IsReachable(id)\n\t\tif e.SpriteGUIRendererComponent ~= nil then\n\t\t\tif reachable then\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.3, 0.55, 0.85, 1)\n\t\t\telse\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)\n\t\t\tend\n\t\tend\n\t\tif e.ButtonComponent ~= nil then\n\t\t\te.ButtonComponent.Enable = reachable\n\t\tend\n\tend\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "RenderMap" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "string", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "id" + } + ], + "Code": "if self.RunActive ~= true then\n\treturn\nend\nif self:IsReachable(id) ~= true then\n\treturn\nend\nself.CurrentNodeId = id\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/MapHud\")\nif hud ~= nil then\n\thud.Enable = false\nend\nlocal node = self.MapNodes[id]\nif node.type == \"shop\" then\n\tself:ShowShop()\nelseif node.type == \"rest\" then\n\tself:ShowRest()\nelse\n\tself.CurrentEnemyId = node.enemy\n\tself:StartCombat()\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "PickNode" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local pool = {}\nfor cid, _ in pairs(self.Cards) do\n\ttable.insert(pool, cid)\nend\nself.ShopChoices = {}\nself.ShopBought = { false, false, false }\nfor i = 1, 3 do\n\tself.ShopChoices[i] = pool[math.random(1, #pool)]\nend\nself.ShopRelic = self.RelicPool[math.random(1, #self.RelicPool)]\nself.ShopRelicBought = false\nself:RenderShop()\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud\")\nif hud ~= nil then\n\thud.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowShop" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "self:SetText(\"/ui/DefaultGroup/ShopHud/Gold\", \"골드 \" .. string.format(\"%d\", self.Gold))\nfor i = 1, 3 do\n\tlocal cid = self.ShopChoices[i]\n\tlocal c = self.Cards[cid]\n\tlocal base = \"/ui/DefaultGroup/ShopHud/Card\" .. tostring(i)\n\tif c ~= nil then\n\t\tself:SetText(base .. \"/Name\", c.name)\n\t\tself:SetText(base .. \"/Cost\", tostring(c.cost))\n\t\tself:SetText(base .. \"/Desc\", c.desc)\n\t\tself:SetText(base .. \"/Price\", string.format(\"%d\", 30) .. \" 골드\")\n\t\tlocal e = _EntityService:GetEntityByPath(base)\n\t\tif e ~= nil and e.SpriteGUIRendererComponent ~= nil then\n\t\t\tif self.ShopBought[i] == true then\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)\n\t\t\telseif c.kind == \"Attack\" then\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)\n\t\t\telseif c.kind == \"Skill\" then\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)\n\t\t\telse\n\t\t\t\te.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)\n\t\t\tend\n\t\tend\n\tend\nend\nlocal rr = self.Relics[self.ShopRelic]\nif rr ~= nil then\n\tself:SetText(\"/ui/DefaultGroup/ShopHud/Relic/Label\", rr.name .. \" — \" .. rr.desc)\n\tself:SetText(\"/ui/DefaultGroup/ShopHud/Relic/Price\", string.format(\"%d\", 60) .. \" 골드\")\n\tlocal re = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud/Relic\")\n\tif re ~= nil and re.SpriteGUIRendererComponent ~= nil then\n\t\tif self.ShopRelicBought == true then\n\t\t\tre.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6)\n\t\telse\n\t\t\tre.SpriteGUIRendererComponent.Color = Color(0.7, 0.55, 0.85, 1)\n\t\tend\n\tend\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "RenderShop" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "if self.ShopRelicBought == true then\n\treturn\nend\nif self.Gold < 60 then\n\treturn\nend\nself.Gold = self.Gold - 60\nself:AddRelic(self.ShopRelic)\nself.ShopRelicBought = true\nself:RenderShop()\nself:RenderRun()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "BuyRelic" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [ + { + "Type": "number", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": "slot" + } + ], + "Code": "if self.ShopBought == nil or self.ShopBought[slot] == true then\n\treturn\nend\nif self.Gold < 30 then\n\treturn\nend\nself.Gold = self.Gold - 30\ntable.insert(self.RunDeck, self.ShopChoices[slot])\nself.ShopBought[slot] = true\nself:RenderShop()\nself:RenderRun()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "BuyCard" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local old = self.PlayerHp\nself.PlayerHp = self.PlayerHp + 30\nif self.PlayerHp > self.PlayerMaxHp then\n\tself.PlayerHp = self.PlayerMaxHp\nend\nlocal healed = self.PlayerHp - old\nself:SetText(\"/ui/DefaultGroup/RestHud/Info\", \"HP \" .. string.format(\"%d\", old) .. \" → \" .. string.format(\"%d\", self.PlayerHp) .. \" (+\" .. string.format(\"%d\", healed) .. \")\")\nself:RenderCombat()\nlocal hud = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RestHud\")\nif hud ~= nil then\n\thud.Enable = true\nend", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "ShowRest" + }, + { + "Return": { + "Type": "void", + "DefaultValue": null, + "SyncDirection": 0, + "Attributes": [], + "Name": null + }, + "Arguments": [], + "Code": "local s = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/ShopHud\")\nif s ~= nil then\n\ts.Enable = false\nend\nlocal r = _EntityService:GetEntityByPath(\"/ui/DefaultGroup/RestHud\")\nif r ~= nil then\n\tr.Enable = false\nend\nself:ShowMap()", + "Scope": 2, + "ExecSpace": 6, + "Attributes": [], + "Name": "LeaveNode" } ], "EntityEventHandlers": [] diff --git a/RootDesk/MyDesk/invincible belief.sprite b/RootDesk/MyDesk/invincible belief.sprite deleted file mode 100644 index b2cce6b..0000000 --- a/RootDesk/MyDesk/invincible belief.sprite +++ /dev/null @@ -1,23 +0,0 @@ -{ - "Id": "", - "GameId": "", - "EntryKey": "sprite://eab37efa7f0d400f94259a2df836eb8a", - "ContentType": "x-mod/sprite", - "Content": "", - "Usage": 0, - "UsePublish": 1, - "UseService": 0, - "CoreVersion": "26.5.0.0", - "StudioVersion": "0.1.0.0", - "DynamicLoading": 0, - "ContentProto": { - "Use": "Json", - "Json": { - "upload_keyname": "30/54/30542a379cb74d2d807104635740a8ea/sprite/eab37efa7f0d400f94259a2df836eb8a/639163110552472416", - "upload_hash": "AAD8C7C4E500FF8E001E85EAB181F3B19605BA9D8C8368DB28919B419515003D", - "name": "invincible belief", - "resource_guid": "eab37efa7f0d400f94259a2df836eb8a", - "resource_version": "6a238b0f1a7908d59b5d8fe4" - } - } -} \ No newline at end of file diff --git a/data/cards.json b/data/cards.json new file mode 100644 index 0000000..2f7b99b --- /dev/null +++ b/data/cards.json @@ -0,0 +1,8 @@ +{ + "cards": { + "Strike": { "name": "타격", "cost": 1, "kind": "Attack", "damage": 6, "desc": "피해 6" }, + "Defend": { "name": "방어", "cost": 1, "kind": "Skill", "block": 5, "desc": "방어도 5" }, + "Bash": { "name": "강타", "cost": 2, "kind": "Attack", "damage": 10, "desc": "피해 10" } + }, + "starterDeck": ["Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash"] +} diff --git a/data/enemies.json b/data/enemies.json new file mode 100644 index 0000000..82d8ff6 --- /dev/null +++ b/data/enemies.json @@ -0,0 +1,33 @@ +{ + "enemies": { + "slime": { + "name": "슬라임", + "maxHp": 45, + "intents": [ + { "kind": "Attack", "value": 10 }, + { "kind": "Attack", "value": 6 }, + { "kind": "Defend", "value": 8 } + ] + }, + "slime_elite": { + "name": "정예 슬라임", + "maxHp": 70, + "intents": [ + { "kind": "Attack", "value": 14 }, + { "kind": "Attack", "value": 8 }, + { "kind": "Defend", "value": 10 } + ] + }, + "slime_boss": { + "name": "슬라임 킹", + "maxHp": 120, + "intents": [ + { "kind": "Attack", "value": 18 }, + { "kind": "Defend", "value": 12 }, + { "kind": "Attack", "value": 10 }, + { "kind": "Attack", "value": 22 } + ] + } + }, + "activeEnemy": "slime" +} diff --git a/data/map.json b/data/map.json new file mode 100644 index 0000000..154719b --- /dev/null +++ b/data/map.json @@ -0,0 +1,12 @@ +{ + "start": ["A", "B"], + "nodes": { + "A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] }, + "B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["C", "D"] }, + "C": { "type": "rest", "row": 2, "col": -1, "next": ["E", "F"] }, + "D": { "type": "shop", "row": 2, "col": 1, "next": ["E", "F"] }, + "E": { "type": "elite", "enemy": "slime_elite", "row": 3, "col": -1, "next": ["BOSS"] }, + "F": { "type": "combat", "enemy": "slime", "row": 3, "col": 1, "next": ["BOSS"] }, + "BOSS": { "type": "boss", "enemy": "slime_boss", "row": 4, "col": 0, "next": [] } + } +} diff --git a/data/relics.json b/data/relics.json new file mode 100644 index 0000000..8f986c4 --- /dev/null +++ b/data/relics.json @@ -0,0 +1,10 @@ +{ + "relics": { + "ironHeart": { "name": "강철 심장", "desc": "전투 시작 시 방어도 +6", "hook": "combatStart", "effect": "block", "value": 6 }, + "energyCore": { "name": "에너지 코어", "desc": "턴 시작 시 에너지 +1", "hook": "turnStart", "effect": "energy", "value": 1 }, + "vampire": { "name": "흡혈 송곳니", "desc": "공격 카드 사용 시 HP +1", "hook": "cardPlayed", "effect": "healOnAttack", "value": 1 }, + "goldIdol": { "name": "황금 우상", "desc": "전투 승리 시 골드 +10", "hook": "combatReward", "effect": "gold", "value": 10 } + }, + "startingRelic": "ironHeart", + "relicPool": ["energyCore", "vampire", "goldIdol"] +} diff --git a/docs/slaymaple_basic_framework.md b/docs/slaymaple_basic_framework.md index 66b5f4e..7f35068 100644 --- a/docs/slaymaple_basic_framework.md +++ b/docs/slaymaple_basic_framework.md @@ -1,42 +1,84 @@ # SlayMaple Basic Framework -This project now has a small deckbuilder roguelike foundation inspired by turn-based card combat games. +This project has a working single-combat deckbuilder loop inspired by turn-based +card combat games. Card play is wired to real combat state (enemy/player HP, +block, enemy intent, win/lose). -## Components +## Current Components (implemented) -- `SlayCardCatalog`: Defines card data, starter deck composition, reward pool, and card cloning. -- `SlayRunState`: Owns persistent run data such as HP, gold, floor, deck, relics, and card rewards. -- `SlayCombatManager`: Runs combat turns, draw/discard/exhaust piles, energy, enemy intents, block, damage, victory, and defeat. +- `SlayDeckController`: The single combat component, attached to the `/common` + entity in `Global/common.gamelogic`. Handles the card-hand UI (draw/discard/ + reshuffle, energy, card-click play), card effects (damage/block), enemy + HP/block/intent, turn flow, and victory/defeat. +- `Monster.codeblock`: A separate field-action monster system (HP, hit event, + respawn). **Not** part of the card combat. -All three components are attached to the `/common` entity in `Global/common.gamelogic`. +All card/deck/combat artifacts (`ui/DefaultGroup.ui`, +`RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic`) are +**generated from a single source, `tools/gen-slaydeck.mjs`** (deterministic +output — do not hand-edit; change the generator and re-run `node +tools/gen-slaydeck.mjs`). -If the Maker session was already open before these files were added, reopen or reload the local workspace so the new codeblock files are imported into the editor state. +If the Maker session was already open before these files changed, reload the +local workspace so the regenerated codeblock/UI files are imported into the +editor state. -## Prototype Flow +## Implemented Combat Loop -1. `SlayRunState` starts a new run with 80 HP and a 10-card starter deck. -2. `SlayCombatManager` starts a demo combat automatically. -3. Each player turn refreshes energy to 3, clears block, rolls enemy intent, and draws 5 cards. -4. Playing a card spends energy, applies damage/block/draw/energy/status effects, then sends the card to discard or exhaust. -5. Ending the turn discards the hand, resolves enemy intent, ticks statuses, and starts the next turn. -6. Winning combat stores the remaining HP back into the run, grants 15 gold, and generates 3 card reward options. +1. `StartCombat` initializes player (HP 80, Block 0) and a single enemy + (HP 45, Block 0) with a deterministic 3-step intent cycle + (Attack 10 → Attack 6 → Defend 8), then starts the first player turn. +2. Each player turn refreshes energy to 3, resets player block, and draws 5 + cards. The enemy's upcoming intent is shown in advance. +3. Cards: Strike (damage 6), Defend (block 5), Bash (damage 10). Each card has + numeric `damage`/`block` fields; starter deck is 10 cards. +4. Playing a card spends energy: `Attack` reduces enemy HP (block absorbs + first); `Skill` adds player block. The card moves to the discard pile. +5. Ending the turn discards the hand, runs the enemy turn (executes the current + intent — attack the player or gain block), advances the intent index, then + starts the next player turn. +6. Enemy HP 0 → victory; player HP 0 → defeat. On combat end, input is locked + and a result text is shown (combat-reward hook reserved for the roguelike + meta — see Planned below). + +> Player HP (80), enemy HP (45), and intent values (10/6/8) are temporary +> placeholders for loop verification. They will move to per-character / +> per-enemy data (see "Data externalization" under Planned). ## Useful Script Calls -From a script attached to the same `/common` entity: +From the `/common` entity (or a Play Test context): ```lua -self.Entity.SlayCombatManager:PlayCard(1, 1) -self.Entity.SlayCombatManager:EndPlayerTurn() -self.Entity.SlayCombatManager:DebugPlayFirstPlayable() -self.Entity.SlayRunState:PickReward(1) -self.Entity.SlayCombatManager:StartCombat("elite") +local c = _EntityService:GetEntityByPath("/common").SlayDeckController +c:PlayCard(1) -- play the hand card in the given slot +c:EndPlayerTurn() -- end turn → enemy turn → next turn +c:StartCombat() -- restart combat (reset state) ``` +## Planned (not yet implemented) — Target Architecture + +The originally envisioned component split for the full roguelike. Currently +**none of these exist**; `SlayDeckController` covers only the card combat above. + +- `SlayCardCatalog`: Card data, starter deck composition, reward pool, card cloning. +- `SlayRunState`: Persistent run data — HP, gold, floor, deck, relics, card rewards. +- `SlayCombatManager`: Turn flow, draw/discard/exhaust piles, energy, enemy + intents, block, damage, victory, and defeat (the role currently played by + `SlayDeckController`). + ## Next Implementation Steps -- Add a combat UI that renders HP, block, energy, enemy intent, and 5 hand-card buttons. -- Add a map node UI with combat, elite, shop, rest, event, and boss node types. -- Add relic definitions and hooks such as `OnCombatStart`, `OnCardPlayed`, `OnTurnStart`, and `OnCombatReward`. -- Add enemy move sets as data instead of the current simple intent pattern. -- Add save/load once the run loop is playable end to end. +- [x] Combat UI rendering HP, block, energy, enemy intent, and hand cards + (done — `SlayDeckController` + CombatHud). +- [x] Card play wired to real damage/block/intent/win-lose (done). +- [ ] Externalize card/enemy data to `data/cards.json` / `data/enemies.json`, + injected by the generator. +- [ ] Monte-Carlo balance simulator `tools/sim-balance.mjs` (requires the data + externalization above). +- [ ] Map node UI with combat, elite, shop, rest, event, and boss node types. +- [ ] Relic definitions and hooks (`OnCombatStart`, `OnCardPlayed`, + `OnTurnStart`, `OnCombatReward`). +- [ ] Enemy move sets as data instead of the current deterministic intent cycle. +- [ ] Run persistence (HP/gold/floor/deck/relics) + save/load once the loop is + playable end to end. diff --git a/docs/superpowers/plans/2026-06-08-card-combat-integration.md b/docs/superpowers/plans/2026-06-08-card-combat-integration.md new file mode 100644 index 0000000..597f101 --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-card-combat-integration.md @@ -0,0 +1,481 @@ +# 카드 전투 통합 (TODO B) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 카드 사용이 실제 적 HP·플레이어 Block·적 의도·승패에 반영되는 단일 전투 루프를 완성한다. + +**Architecture:** 모든 변경은 `tools/gen-slaydeck.mjs` 단일 생성기에서 만든다. 적/플레이어 전투 상태는 `SlayDeckController` codeblock 내부 속성으로 보유(필드 `Monster.codeblock`과 분리). UI는 `CombatHud` 그룹으로 DeckHud와 별도 생성. 수치(플레이어 80 / 적 45 / 의도 10·6·방8)는 임시 placeholder. + +**Tech Stack:** Node.js ESM 생성기(`gen-slaydeck.mjs`), MSW Lua codeblock, MSW UI JSON. 검증은 `node --check` + 재생성 + sha1 결정성 + 메이커 Play. + +--- + +## File Structure + +- Modify: `tools/gen-slaydeck.mjs` — 유일한 변경 대상. + - `upsertUi()`: `CombatHud` 그룹(적/플레이어 패널·결과 텍스트) 생성 추가, 정리 필터 확장. + - `writeCodeblocks()`: `SlayDeckController` 속성·메서드 추가/수정. +- 생성물(자동, 직접 편집 금지): `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic`. + +검증 한계: MSW codeblock Lua는 단위 테스트 러너가 없다. 자동 검증은 생성기 문법·재생성·결정성·JSON 유효성까지, 실제 동작은 메이커 Play(사용자)로 확인. + +--- + +### Task 1: 카드 데이터 수치화 (Cards 테이블 + UI 카드 배열) + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`upsertUi` 내 `cards` 배열, `writeCodeblocks` 내 `StartCombat`의 `self.Cards`) + +- [ ] **Step 1: `upsertUi`의 카드 배열은 표시용 그대로 두되, codeblock `Cards`에 수치 필드 추가** + +`writeCodeblocks()`의 `StartCombat` 메서드 코드에서 `self.Cards` 정의를 아래로 교체: + +```lua +self.Cards = { + Strike = { name = "타격", cost = 1, desc = "피해 6", kind = "Attack", damage = 6 }, + Defend = { name = "방어", cost = 1, desc = "방어도 5", kind = "Skill", block = 5 }, + Bash = { name = "강타", cost = 2, desc = "피해 10", kind = "Attack", damage = 10 }, +} +``` + +- [ ] **Step 2: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 (출력 없음, exit 0) + +- [ ] **Step 3: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(B): 카드 데이터에 damage/block 수치 필드 추가" +``` + +--- + +### Task 2: 전투 상태 속성 + StartCombat 초기화 + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`writeCodeblocks` 속성 배열, `StartCombat` 메서드) + +- [ ] **Step 1: codeblock 속성 추가** + +`codeblock('SlayDeckController', ...)`의 properties 배열 끝에 추가: + +```js + prop('number', 'PlayerHp', '0'), + prop('number', 'PlayerMaxHp', '80'), + prop('number', 'PlayerBlock', '0'), + prop('number', 'EnemyHp', '0'), + prop('number', 'EnemyMaxHp', '45'), + prop('number', 'EnemyBlock', '0'), + prop('number', 'EnemyIntentIndex', '1'), + prop('boolean', 'CombatOver', 'false'), + prop('any', 'EnemyIntents'), + prop('any', 'EnemyName'), +``` + +- [ ] **Step 2: `StartCombat`에 전투 상태 초기화 추가** + +`StartCombat` 코드의 맨 위(`self.MaxEnergy = 3` 직후)에 삽입: + +```lua +self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.PlayerBlock = 0 +self.EnemyName = "슬라임" +self.EnemyMaxHp = 45 +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = { + { kind = "Attack", value = 10 }, + { kind = "Attack", value = 6 }, + { kind = "Defend", value = 8 }, +} +self.EnemyIntentIndex = 1 +self.CombatOver = false +``` + +그리고 `StartCombat` 끝(`self:StartPlayerTurn()` 직전)에 `self:RenderCombat()` 추가. + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(B): 플레이어/적 전투 상태 속성·초기화 추가" +``` + +--- + +### Task 3: 전투 헬퍼 메서드 (데미지/적턴/승패/렌더) + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`writeCodeblocks` methods 배열에 신규 메서드 추가) + +`SetText`는 엔티티 nil 가드가 있어, 참조하는 UI가 Task 5에서 생성되기 전이어도 안전(no-op). + +- [ ] **Step 1: 신규 메서드들을 methods 배열에 추가 (`Toast` 메서드 정의 뒤)** + +```js + method('DealDamageToEnemy', `local dmg = amount +if self.EnemyBlock > 0 then + local absorbed = math.min(self.EnemyBlock, dmg) + self.EnemyBlock = self.EnemyBlock - absorbed + dmg = dmg - absorbed +end +self.EnemyHp = self.EnemyHp - dmg +if self.EnemyHp < 0 then + self.EnemyHp = 0 +end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]), + method('DealDamageToPlayer', `local dmg = amount +if self.PlayerBlock > 0 then + local absorbed = math.min(self.PlayerBlock, dmg) + self.PlayerBlock = self.PlayerBlock - absorbed + dmg = dmg - absorbed +end +self.PlayerHp = self.PlayerHp - dmg +if self.PlayerHp < 0 then + self.PlayerHp = 0 +end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]), + method('EnemyTurn', `self.EnemyBlock = 0 +local intent = self.EnemyIntents[self.EnemyIntentIndex] +if intent ~= nil then + if intent.kind == "Attack" then + self:DealDamageToPlayer(intent.value) + elseif intent.kind == "Defend" then + self.EnemyBlock = self.EnemyBlock + intent.value + end +end +self.EnemyIntentIndex = self.EnemyIntentIndex + 1 +if self.EnemyIntentIndex > #self.EnemyIntents then + self.EnemyIntentIndex = 1 +end +self:RenderCombat()`), + method('CheckCombatEnd', `if self.EnemyHp <= 0 then + self.CombatOver = true + self:ShowResult("승리!") + -- TODO(E): 전투 보상 훅 — 카드 보상/골드/유물 선택 진입점 +elseif self.PlayerHp <= 0 then + self.CombatOver = true + self:ShowResult("패배...") +end`), + method('ShowResult', `self:SetText("/ui/DefaultGroup/CombatHud/Result", text) +local entity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/Result") +if entity ~= nil then + entity.Enable = true +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'text' }]), + method('RenderCombat', `self:SetText("/ui/DefaultGroup/CombatHud/EnemyName", self.EnemyName) +self:SetText("/ui/DefaultGroup/CombatHud/EnemyHp", "HP " .. tostring(self.EnemyHp) .. "/" .. tostring(self.EnemyMaxHp)) +self:SetText("/ui/DefaultGroup/CombatHud/EnemyBlock", "방어 " .. tostring(self.EnemyBlock)) +local intent = self.EnemyIntents[self.EnemyIntentIndex] +local intentText = "" +if intent ~= nil then + if intent.kind == "Attack" then + intentText = "의도: 공격 " .. tostring(intent.value) + elseif intent.kind == "Defend" then + intentText = "의도: 방어 " .. tostring(intent.value) + end +end +self:SetText("/ui/DefaultGroup/CombatHud/EnemyIntent", intentText) +self:SetText("/ui/DefaultGroup/CombatHud/PlayerHp", "HP " .. tostring(self.PlayerHp) .. "/" .. tostring(self.PlayerMaxHp)) +self:SetText("/ui/DefaultGroup/CombatHud/PlayerBlock", "방어 " .. tostring(self.PlayerBlock))`), +``` + +- [ ] **Step 2: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 3: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(B): 데미지/적턴/승패/전투렌더 헬퍼 메서드 추가" +``` + +--- + +### Task 4: 턴 흐름 배선 (PlayCard 효과·EndPlayerTurn·StartPlayerTurn) + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`StartPlayerTurn`, `EndPlayerTurn`, `PlayCard` 메서드 코드) + +- [ ] **Step 1: `StartPlayerTurn` 교체** + +```lua +self.Turn = self.Turn + 1 +self.Energy = self.MaxEnergy +self.PlayerBlock = 0 +self:DrawCards(5) +self:RenderHand(true) +self:RenderCombat() +``` + +- [ ] **Step 2: `EndPlayerTurn` 교체** + +```lua +if self.CombatOver == true then + return +end +for i = 1, #self.Hand do + table.insert(self.DiscardPile, self.Hand[i]) +end +self.Hand = {} +self:RenderHand(false) +self:RenderPiles() +self:EnemyTurn() +self:CheckCombatEnd() +if self.CombatOver == true then + return +end +_TimerService:SetTimerOnce(function() self:StartPlayerTurn() end, 0.45) +``` + +- [ ] **Step 3: `PlayCard` 효과 분기 교체** + +`PlayCard` 코드를 아래로 교체(에너지 차감 후 Toast 대신 효과 적용): + +```lua +if self.CombatOver == true then + return +end +if self.Hand == nil then + return +end +local cardId = self.Hand[slot] +if cardId == nil then + return +end +local c = self.Cards[cardId] +if c == nil then + return +end +if self.Energy < c.cost then + self:Toast("에너지가 부족합니다") + return +end +self.Energy = self.Energy - c.cost +if c.kind == "Attack" then + if c.damage ~= nil then + self:DealDamageToEnemy(c.damage) + end +elseif c.kind == "Skill" then + if c.block ~= nil then + self.PlayerBlock = self.PlayerBlock + c.block + end +end +table.remove(self.Hand, slot) +table.insert(self.DiscardPile, cardId) +self:RenderHand(false) +self:RenderPiles() +self:RenderCombat() +self:CheckCombatEnd() +``` + +- [ ] **Step 4: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 5: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(B): PlayCard 효과 분기·적턴·승패 턴흐름 배선" +``` + +--- + +### Task 5: CombatHud UI 엔티티 생성 + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`upsertUi`: 정리 필터 확장 + CombatHud 그룹 생성) + +- [ ] **Step 1: 정리 필터 확장** + +`upsertUi()` 시작부의 필터를 CombatHud도 제거하도록 교체: + +```js + ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud')); +``` + +- [ ] **Step 2: DeckHud `hud` push 직후, CombatHud 엔티티 생성 블록 추가** + +`ui.ContentProto.Entities.push(...hud);` 직전에 아래 블록 삽입(헬퍼 `entity`/`transform`/`sprite`/`text`/`guid` 재사용): + +```js + const PANEL_BG = { r: 0.08, g: 0.09, b: 0.11, a: 0.78 }; + const combat = []; + combat.push(entity({ + id: guid('cmb', 0), + path: '/ui/DefaultGroup/CombatHud', + modelId: 'uiempty', + entryId: 'UIEmpty', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 4, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: TRANSPARENT }), + ], + })); + // 적 패널 배경 + combat.push(entity({ + id: guid('cmb', 1), + path: '/ui/DefaultGroup/CombatHud/EnemyBg', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 380, y: 170 }, pos: { x: 0, y: 300 }, align: ALIGN_CENTER }), + sprite({ color: PANEL_BG, type: 1 }), + ], + })); + const enemyTexts = [ + ['EnemyName', { x: 0, y: 58 }, { x: 360, y: 44 }, '슬라임', 28, true, GOLD, 1], + ['EnemyHp', { x: 0, y: 16 }, { x: 360, y: 40 }, 'HP 45/45', 24, true, { r: 1, g: 1, b: 1, a: 1 }, 2], + ['EnemyBlock', { x: 0, y: -20 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }, 3], + ['EnemyIntent', { x: 0, y: -56 }, { x: 360, y: 38 }, '의도: 공격 10', 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }, 4], + ]; + let cmbN = 2; + for (const [suffix, pos, size, value, fontSize, bold, color] of enemyTexts) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: enemyTexts.findIndex(([s]) => s === suffix) + 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize, bold, color }), + ], + })); + } + // 플레이어 패널 배경 + 텍스트 + combat.push(entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/PlayerBg', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 5, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 110 }, pos: { x: -760, y: -260 }, align: ALIGN_CENTER }), + sprite({ color: PANEL_BG, type: 1 }), + ], + })); + const playerTexts = [ + ['PlayerHp', { x: -760, y: -238 }, { x: 280, y: 44 }, 'HP 80/80', 26, true, { r: 1, g: 1, b: 1, a: 1 }], + ['PlayerBlock', { x: -760, y: -284 }, { x: 280, y: 38 }, '방어 0', 22, false, { r: 0.6, g: 0.8, b: 1, a: 1 }], + ]; + for (const [suffix, pos, size, value, fontSize, bold, color] of playerTexts) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 6 + playerTexts.findIndex(([s]) => s === suffix), + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize, bold, color }), + ], + })); + } + // 결과 텍스트 (기본 숨김) + const result = entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/Result', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 8, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 140 }, pos: { x: 0, y: 120 } }), + sprite({ color: TRANSPARENT }), + text({ value: '', fontSize: 64, bold: true, color: GOLD, alignment: 4 }), + ], + }); + result.jsonString.enable = false; + combat.push(result); + ui.ContentProto.Entities.push(...combat); +``` + +`guid` 프리픽스 `'cmb'`를 위해 `guid()`의 ns 매핑에 분기 추가: + +```js + const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : 0xfe; +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(B): CombatHud(적/플레이어 패널·결과) UI 엔티티 생성" +``` + +--- + +### Task 6: 재생성 + 검증 + +**Files:** 생성물 3종 (생성기 실행 결과) + +- [ ] **Step 1: 생성기 실행** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 생성물 JSON 유효성 확인** + +Run: `node -e "JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); console.log('JSON OK')"` +Expected: `JSON OK` + +- [ ] **Step 3: 결정성 확인 (2회 실행 동일)** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: CombatHud 엔티티·전투 메서드 생성 확인** + +Run: `grep -c "CombatHud" ui/DefaultGroup.ui; grep -c "DealDamageToEnemy\|EnemyTurn\|RenderCombat" RootDesk/MyDesk/SlayDeckController.codeblock` +Expected: 두 값 모두 > 0 + +- [ ] **Step 5: 의도한 파일만 변경됐는지 확인** + +Run: `git status --short` +Expected: `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (그리고 필요 시 `Global/common.gamelogic`)만 변경. + +- [ ] **Step 6: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock Global/common.gamelogic +git commit -m "재생성(B): 카드 전투 통합 — 적/플레이어 전투 상태·CombatHud 반영" +``` + +- [ ] **Step 7: 메이커 Play 수동 검증 (사용자)** + +메이커에서 로컬 워크스페이스 reload 후 Play: +- 타격 카드 클릭 → 적 HP 감소(적 Block 있으면 먼저 차감). +- 방어 카드 클릭 → 플레이어 `방어` 수치 증가. +- 턴 종료 → 적이 표시된 의도대로 공격(플레이어 Block이 피해 흡수) 또는 방어, 다음 의도 갱신. +- 적 HP 0 → "승리!" 표시·입력 잠금 / 플레이어 HP 0 → "패배..." 표시·입력 잠금. + +--- + +## Self-Review + +- **Spec coverage:** 전투 상태(Task 2), 카드 수치화(Task 1), 효과 분기(Task 4), 적 의도·적 턴(Task 3·4), 승패(Task 3·4), UI 노출(Task 5) — 스펙 5개 절 모두 태스크로 매핑됨. 검증은 Task 6. +- **Placeholder scan:** 모든 코드 단계에 실제 코드 포함. "TODO(E)"는 의도된 미래 훅 주석(스펙 명시)으로 placeholder 아님. +- **Type consistency:** UI 경로(`/ui/DefaultGroup/CombatHud/EnemyHp` 등)가 codeblock `RenderCombat`/`ShowResult`와 Task 5 생성 경로에서 동일. 메서드명(`DealDamageToEnemy`/`DealDamageToPlayer`/`EnemyTurn`/`CheckCombatEnd`/`ShowResult`/`RenderCombat`)이 호출부(Task 4)와 정의부(Task 3)에서 일치. 카드 필드(`damage`/`block`/`kind`)가 Cards 정의(Task 1)와 PlayCard 사용(Task 4)에서 일치. diff --git a/docs/superpowers/plans/2026-06-08-deck-controller-fixes.md b/docs/superpowers/plans/2026-06-08-deck-controller-fixes.md new file mode 100644 index 0000000..166a5ff --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-deck-controller-fixes.md @@ -0,0 +1,256 @@ +# 덱 컨트롤러 코드리뷰 수정 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 코드리뷰 6건(①self바인딩 ②Card5통일 ③카드클릭=사용 ④카드데이터단일화 ⑤매직넘버 ⑥pcall)을 `tools/gen-slaydeck.mjs`에서 수정·재생성한다. + +**Architecture:** 모든 산출물(카드 UI·DeckHud·`SlayDeckController.codeblock`·`common.gamelogic`)을 생성하는 `tools/gen-slaydeck.mjs` 단일 소스를 수정하고 재실행한다. DRY는 카드 정의를 codeblock의 `self.Cards` 테이블 프로퍼티로 단일화하고, 카드 클릭은 카드 엔티티에 `ButtonComponent`를 추가한 뒤 `PlayCard(slot)` 메서드를 클로저로 연결해 구현한다. + +**Tech Stack:** Node.js 생성기, MSW codeblock(MapleScript/Lua), msw-maker-mcp(검증). + +--- + +## File Structure +- Modify: `tools/gen-slaydeck.mjs` — 모든 수정의 단일 소스. +- 재생성(출력): `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic`. + +기준: codeblock 메서드는 `method('Name', ``, [args])`로 정의되고 끝에서 전부 `ExecSpace=6`로 설정됨. 카드 엔티티(Card1~5)는 `upsertUi`의 루프가 스타일링함. `button()` 헬퍼 존재. + +--- + +### Task 1: 생성기 수정 (① ③ ④ ⑥ + ⑤ 일부) + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: 카드에 ButtonComponent + raycast 추가 (③ 클릭 가능)** + +`upsertUi`의 카드 루프에서 `sp.Color = cards[i - 1].tint;` 줄 바로 다음에 아래를 추가: +```js + sp.RaycastTarget = true; + const comps = card.jsonString['@components']; + if (!comps.some((c) => c['@type'] === 'MOD.Core.ButtonComponent')) { + comps.push(button()); + } + if (!card.componentNames.includes('MOD.Core.ButtonComponent')) { + card.componentNames += ',MOD.Core.ButtonComponent'; + } +``` + +- [ ] **Step 2: `Cards` 프로퍼티 추가 (④ 단일화 준비)** + +`writeCodeblocks`의 properties 배열(`prop('any', 'EndTurnHandler')` 가 있는 배열)에 항목 추가: +```js + prop('any', 'Cards'), +``` + +- [ ] **Step 3: StartCombat 메서드 교체 (④ 카드 테이블 정의)** + +`method('StartCombat', ...)` 의 Lua 본문을 아래로 교체: +``` +self.MaxEnergy = 3 +self.Turn = 0 +self.DiscardPile = {} +self.Hand = {} +self.Cards = { + Strike = { name = "타격", cost = 1, desc = "피해 6", kind = "Attack" }, + Defend = { name = "방어", cost = 1, desc = "방어도 5", kind = "Skill" }, + Bash = { name = "강타", cost = 2, desc = "피해 10", kind = "Attack" }, +} +self.DrawPile = { "Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash" } +self:Shuffle(self.DrawPile) +self:BindButtons() +self:StartPlayerTurn() +``` + +- [ ] **Step 4: BindButtons 교체 (① 클로저 + ③ 카드 클릭 바인딩)** + +`method('BindButtons', ...)` 의 Lua 본문을 아래로 교체: +``` +local endTurn = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton") +if endTurn ~= nil and endTurn.ButtonComponent ~= nil then + if self.EndTurnHandler ~= nil then + endTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler) + self.EndTurnHandler = nil + end + self.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end) +end +for i = 1, 5 do + local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i)) + if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then + cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end) + end +end +``` + +- [ ] **Step 5: ApplyCardVisual 교체 (④ self.Cards 사용 + ⑥ pcall 제거)** + +`method('ApplyCardVisual', ...)` 의 Lua 본문을 아래로 교체(인자 slot, cardId 유지): +``` +local c = self.Cards[cardId] +if c == nil then + c = { name = cardId, cost = 0, desc = "", kind = "Skill" } +end +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Cost", tostring(c.cost)) +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Name", c.name) +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Desc", c.desc) +local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot)) +if cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then + if c.kind == "Attack" then + cardEntity.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + cardEntity.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + cardEntity.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end +end +``` + +- [ ] **Step 6: PlayCard + Toast 메서드 추가 (③)** + +`method('AnimateCardFrom', ...)` 항목 다음(메서드 배열 안)에 두 메서드를 추가: +```js + method('PlayCard', `if self.Hand == nil then + return +end +local cardId = self.Hand[slot] +if cardId == nil then + return +end +local c = self.Cards[cardId] +if c == nil then + return +end +if self.Energy < c.cost then + self:Toast("에너지가 부족합니다") + return +end +self.Energy = self.Energy - c.cost +self:Toast(c.name .. " — " .. c.desc) +table.remove(self.Hand, slot) +table.insert(self.DiscardPile, cardId) +self:RenderHand(false) +self:RenderPiles()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('Toast', `log(message)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'message' }]), +``` + +(⑤: 손패/슬롯 수 5는 UI 카드 엔티티가 정확히 5개라 고정값으로 둠 — 별도 상수 불필요. 시작 에너지/MaxEnergy는 이미 프로퍼티.) + +- [ ] **Step 7: 구문 확인 + 커밋** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node --check tools/gen-slaydeck.mjs +git add tools/gen-slaydeck.mjs +git commit -m "덱 컨트롤러 생성기: 핸들러 클로저화·카드데이터 단일화·카드클릭 사용·pcall 제거" +``` +Expected: `node --check` 무출력(exit 0). + +--- + +### Task 2: 재생성 + 데이터 검증 + +**Files:** Modify `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock`, `Global/common.gamelogic` + +- [ ] **Step 1: 재생성** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node tools/gen-slaydeck.mjs +``` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: codeblock 검증** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8'));const ms=j.ContentProto.Json.Methods;const names=ms.map(m=>m.Name);console.log('has PlayCard:',names.includes('PlayCard'));console.log('has Toast:',names.includes('Toast'));const bind=ms.find(m=>m.Name==='BindButtons').Code;console.log('endturn closure:',bind.includes('function() self:EndPlayerTurn() end'));console.log('card click bind:',bind.includes('function() self:PlayCard(i) end'));const av=ms.find(m=>m.Name==='ApplyCardVisual').Code;console.log('no pcall:',!av.includes('pcall'));console.log('uses self.Cards:',av.includes('self.Cards[cardId]'));const sc=ms.find(m=>m.Name==='StartCombat').Code;console.log('Cards table:',sc.includes('self.Cards ='))" +``` +Expected: 모두 `true`. + +- [ ] **Step 3: UI 검증 (카드 버튼 + Card5 통일)** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node -e "const j=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8'));const E=j.ContentProto.Entities;let okBtn=true,okImg=true;for(let i=1;i<=5;i++){const c=E.find(e=>e.path==='/ui/DefaultGroup/CardHand/Card'+i);if(!c){okBtn=false;continue;}if(!(c.componentNames||'').includes('MOD.Core.ButtonComponent'))okBtn=false;const sp=c.jsonString['@components'].find(x=>x['@type']==='MOD.Core.SpriteGUIRendererComponent');if(sp.ImageRUID.DataId!=='')okImg=false;}const c5=E.find(e=>e.path==='/ui/DefaultGroup/CardHand/Card5');const hasDesc=E.some(e=>e.path==='/ui/DefaultGroup/CardHand/Card5/Desc');console.log('all cards have Button:',okBtn);console.log('all cards no image (uniform):',okImg);console.log('Card5 has Desc child:',hasDesc)" +``` +Expected: `all cards have Button: true`, `all cards no image (uniform): true`, `Card5 has Desc child: true`. + +- [ ] **Step 4: JSON 유효성 + 커밋** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node -e "JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8'));JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8'));JSON.parse(require('fs').readFileSync('Global/common.gamelogic','utf8'));console.log('JSON ok')" +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock Global/common.gamelogic +git commit -m "재생성: 카드 클릭 사용·균일 카드·핸들러 수정 반영" +``` +Expected: `JSON ok`. + +--- + +### Task 3: Maker Play 검증 (컨트롤러) + +**Files:** 없음 + +- [ ] **Step 1: reload**: `maker_refresh_workspace`. +- [ ] **Step 2: 시작 맵 활성화 확인**: `maker_get_current_map`. (어느 맵이든 카드 UI는 전역이라 표시됨) +- [ ] **Step 3: play**: `maker_play`. +- [ ] **Step 4: 클릭 시뮬레이션 + 상태 확인**: `maker_execute_script`(client)로 PlayCard 직접 호출해 동작 확인: + ```lua + local ctrl = _EntityService:GetEntityByPath("/common") + -- 초기 상태 + local c = ctrl.SlayDeckController + log("BEFORE energy="..tostring(c.Energy).." hand="..tostring(#c.Hand).." discard="..tostring(#c.DiscardPile)) + c:PlayCard(1) + log("AFTER energy="..tostring(c.Energy).." hand="..tostring(#c.Hand).." discard="..tostring(#c.DiscardPile)) + ``` + → `maker_logs(normal)`에서 카드 사용 후 energy 감소·hand 감소·discard 증가 확인. (또는 `maker_mouse_input`으로 카드 클릭) +- [ ] **Step 5: screenshot**: `maker_screenshot` → Read로 5장 균일·DeckHud(에너지/덱 카운트) 확인. +- [ ] **Step 6: stop**: `maker_stop`. + +문제 시: 핸들러 self·PlayCard 동작 로그로 진단 후 Task 1 수정·재생성. + +--- + +### Task 4: stash 복구 + 무결성 검증 + +**Files:** `map/map02.map`, `map/map05.map`, `map/map06.map`, `map/map07.map`, `map/map10.map`, `map/map11.map` (복구 대상) + +- [ ] **Step 1: stash 적용** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +git stash list +git stash apply 2>&1 | head -20 +``` +(충돌 시 해당 파일은 main 버전 유지하고 stash 변경만 수동 반영하거나, 무의미하면 제외 — 아래 검증으로 판단) + +- [ ] **Step 2: 무결성 검증 (몬스터/타일셋 유지 확인)** + +```bash +cd "C:/Users/jaeoh/Desktop/workspace/slaymaple" +node -e "const old=['8ef238e0d0ca4bb783aca526cff35d11','6c7130f51a654803a1c39cbe30e2f427','3e76c89ae8e7477ca871f5bbcd6f6f29','6d381bea1bcb4504b518a1fbfa0904ac','c96c11f9a3f845a4b6a27d9ca10ab103'];for(const t of ['02','05','06','07','10','11']){const j=JSON.parse(require('fs').readFileSync('map/map'+t+'.map','utf8'));const E=j.ContentProto.Entities;const ms=E.filter(e=>(e.componentNames||'').includes('script.Monster'));const sprs=ms.map(m=>m.jsonString['@components'].find(c=>c['@type']==='MOD.Core.SpriteRendererComponent').SpriteRUID);const okNoOld=sprs.every(s=>!old.includes(s));const ts=E.find(e=>(e.path||'').endsWith('/TileMap')).jsonString['@components'].find(c=>c['@type']==='MOD.Core.TileMapComponent').TileSetRUID.DataId;console.log('map'+t,'monsters='+ms.length,'noOldSprite='+okNoOld,'tileset='+(ts!=='9dfea3808bbd49a5877d8624df21b1c7'))}" +``` +Expected: 각 맵 `monsters=2`, `noOldSprite=true`, `tileset=true`. (= 몬스터/타일셋 작업 유지됨) + +- [ ] **Step 3: 판정 및 커밋** + +- 무결성 OK → 복구분 커밋: + ```bash + git add map/map02.map map/map05.map map/map06.map map/map07.map map/map10.map map/map11.map + git commit -m "Maker 세션 재저장분(맵 02/05/06/07/10/11) 복구 포함" + git stash drop + ``` +- 무결성 실패(작업 되돌려짐/손상) → 복구 취소하고 사용자에게 보고: + ```bash + git checkout -- map/map02.map map/map05.map map/map06.map map/map07.map map/map10.map map/map11.map + ``` + (stash는 보존) + +--- + +## 검증 요약 +- 생성기 `node --check` 통과 +- codeblock: PlayCard/Toast 존재, EndTurn·카드클릭 클로저, self.Cards 사용, pcall 없음 +- UI: Card1~5 ButtonComponent+raycast, 5장 균일(이미지 없음·Desc 존재) +- Maker Play: PlayCard 호출 시 energy↓·hand↓·discard↑, 5장 균일 렌더 +- stash 복구분 무결성(몬스터2·old미사용·타일셋교체) 검증 후 포함 diff --git a/docs/superpowers/plans/2026-06-09-balance-simulator.md b/docs/superpowers/plans/2026-06-09-balance-simulator.md new file mode 100644 index 0000000..d798dbe --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-balance-simulator.md @@ -0,0 +1,475 @@ +# AI 전투 시뮬레이터 (TODO F) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** `data/*.json`을 입력으로 전투를 몬테카를로 N회 시뮬레이션해 승률·턴·OP 카드 리포트를 출력하는 오프라인 CLI `tools/sim-balance.mjs`. + +**Architecture:** 순수 함수(PRNG·applyDamage·chooseAction·simulateCombat·runBatch)로 분리해 `node:test`로 단위 테스트. CLI main은 직접 실행 시에만 동작. 전투 규칙은 gen-slaydeck.mjs의 Lua를 JS로 미러, 데이터는 D의 JSON 공유. + +**Tech Stack:** Node.js ESM, `node:test`+`node:assert`. 검증은 단위 테스트 + CLI 실행 + 결정성 + 데이터 반영. + +--- + +## File Structure + +- Create: `tools/sim-balance.mjs` — 시뮬레이터(엔진·정책·집계·리포트·CLI). 순수 함수 export. +- Create: `tools/sim-balance.test.mjs` — 단위 테스트(node:test). + +전투 규칙은 `tools/gen-slaydeck.mjs` Lua와 중복 → 파일 상단 동기화 주석. + +--- + +### Task 1: PRNG·applyDamage·loadData (기반 순수 함수) + +**Files:** +- Create: `tools/sim-balance.mjs` +- Create: `tools/sim-balance.test.mjs` + +- [ ] **Step 1: 테스트 작성 `tools/sim-balance.test.mjs`** + +```js +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { mulberry32, applyDamage } from './sim-balance.mjs'; + +test('applyDamage: 방어 우선 차감 후 hp', () => { + assert.deepEqual(applyDamage(80, 0, 10), { hp: 70, block: 0 }); + assert.deepEqual(applyDamage(80, 5, 10), { hp: 75, block: 0 }); + assert.deepEqual(applyDamage(80, 12, 10), { hp: 80, block: 2 }); + assert.deepEqual(applyDamage(3, 0, 10), { hp: 0, block: 0 }); +}); + +test('mulberry32: 동일 시드 동일 수열', () => { + const a = mulberry32(1), b = mulberry32(1); + assert.equal(a(), b()); + assert.equal(a(), b()); +}); +``` + +- [ ] **Step 2: 테스트 실패 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: FAIL (`Cannot find module './sim-balance.mjs'` 또는 export 없음) + +- [ ] **Step 3: `tools/sim-balance.mjs` 작성(기반부)** + +```js +// AI 전투 밸런스 시뮬레이터 — 오프라인 몬테카를로. +// ⚠️ 전투 규칙은 tools/gen-slaydeck.mjs 의 Lua(SlayDeckController)와 동기화 유지할 것. +import { readFileSync } from 'node:fs'; + +export const PLAYER_HP = 80; // 데이터 미포함 placeholder (codeblock과 일치) +export const ENERGY = 3; +export const HAND_SIZE = 5; +export const MAX_TURNS = 100; + +export function mulberry32(seed) { + let a = seed >>> 0; + return function () { + a |= 0; a = (a + 0x6D2B79F5) | 0; + let t = Math.imul(a ^ (a >>> 15), 1 | a); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +export function shuffle(arr, rng) { + const a = arr.slice(); + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(rng() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +// 방어 우선 차감 후 hp 적용 → { hp, block } +export function applyDamage(hp, block, amount) { + let dmg = amount; + if (block > 0) { + const absorbed = Math.min(block, dmg); + block -= absorbed; + dmg -= absorbed; + } + hp -= dmg; + if (hp < 0) hp = 0; + return { hp, block }; +} + +export function loadData() { + const cardsData = JSON.parse(readFileSync('data/cards.json', 'utf8')); + const enemiesData = JSON.parse(readFileSync('data/enemies.json', 'utf8')); + const enemy = enemiesData.enemies[enemiesData.activeEnemy]; + if (!enemy) throw new Error(`activeEnemy 없음: ${enemiesData.activeEnemy}`); + return { cards: cardsData.cards, starterDeck: cardsData.starterDeck, enemy }; +} +``` + +- [ ] **Step 4: 테스트 통과 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: PASS (2 tests) + +- [ ] **Step 5: 커밋** + +```bash +git add tools/sim-balance.mjs tools/sim-balance.test.mjs +git commit -m "sim-balance(F): PRNG·applyDamage·loadData 기반 함수 + 테스트" +``` + +--- + +### Task 2: chooseAction 정책 (휴리스틱 A) + +**Files:** +- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs` + +- [ ] **Step 1: 테스트 추가 (test.mjs 하단)** + +```js +import { chooseAction } from './sim-balance.mjs'; + +const CARDS = { + Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 }, + Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 }, + Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 }, +}; + +test('chooseAction: 치사 가능하면 공격 선택', () => { + // 적 hp 5, block 0, 손패 Strike(6) → 공격(인덱스 0) + const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 5, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, 0); +}); + +test('chooseAction: 치사 불가 + 적 공격 의도면 방어 선택', () => { + // 적 hp 40(이번 턴 못 죽임), 의도 공격 → Defend(인덱스 1) + const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 40, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, 1); +}); + +test('chooseAction: 적 방어 의도면 공격 우선', () => { + const idx = chooseAction(['Defend', 'Strike'], CARDS, 3, 40, 0, { kind: 'Defend', value: 8 }); + assert.equal(idx, 1); +}); + +test('chooseAction: 사용 가능 카드 없으면 -1', () => { + const idx = chooseAction(['Bash'], CARDS, 1, 40, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, -1); +}); +``` + +- [ ] **Step 2: 테스트 실패 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: FAIL (`chooseAction is not a function`) + +- [ ] **Step 3: 구현 추가 (sim-balance.mjs)** + +```js +// 손패에서 다음에 낼 카드의 인덱스 반환(-1=턴 종료). hand=카드 id 배열. +export function chooseAction(hand, cards, energy, enemyHp, enemyBlock, enemyIntent) { + const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id].cost <= energy); + const attacks = entries.filter((x) => cards[x.id].kind === 'Attack'); + const skills = entries.filter((x) => cards[x.id].kind === 'Skill'); + const dmgEff = (x) => (cards[x.id].damage || 0) / cards[x.id].cost; + const blkEff = (x) => (cards[x.id].block || 0) / cards[x.id].cost; + const bestBy = (list, fn) => list.slice().sort((a, b) => fn(b) - fn(a))[0]; + + // 1) 치사: 에너지 한도 내 효율순 공격 데미지 합 >= 적 유효 hp? + let e = energy, lethalDmg = 0; + for (const x of attacks.slice().sort((a, b) => dmgEff(b) - dmgEff(a))) { + if (cards[x.id].cost <= e) { e -= cards[x.id].cost; lethalDmg += cards[x.id].damage || 0; } + } + if (attacks.length && lethalDmg >= enemyHp + enemyBlock) return bestBy(attacks, dmgEff).i; + + // 2) 적 공격 의도면 방어 우선 + if (enemyIntent && enemyIntent.kind === 'Attack' && skills.length) return bestBy(skills, blkEff).i; + + // 3) 공격 우선, 없으면 스킬, 없으면 종료 + if (attacks.length) return bestBy(attacks, dmgEff).i; + if (skills.length) return bestBy(skills, blkEff).i; + return -1; +} +``` + +- [ ] **Step 4: 테스트 통과 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: PASS (6 tests) + +- [ ] **Step 5: 커밋** + +```bash +git add tools/sim-balance.mjs tools/sim-balance.test.mjs +git commit -m "sim-balance(F): 플레이어 휴리스틱 정책 chooseAction + 테스트" +``` + +--- + +### Task 3: simulateCombat 엔진 + +**Files:** +- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs` + +- [ ] **Step 1: 테스트 추가** + +```js +import { simulateCombat, mulberry32 as m32 } from './sim-balance.mjs'; + +const DATA = { + cards: { + Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 }, + Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 }, + Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 }, + }, + starterDeck: ['Strike','Strike','Strike','Strike','Strike','Defend','Defend','Defend','Defend','Bash'], + enemy: { name: '슬라임', maxHp: 45, intents: [ + { kind: 'Attack', value: 10 }, { kind: 'Attack', value: 6 }, { kind: 'Defend', value: 8 }, + ] }, +}; + +test('simulateCombat: 결정적 결과(동일 시드)', () => { + const r1 = simulateCombat(DATA, m32(1)); + const r2 = simulateCombat(DATA, m32(1)); + assert.deepEqual(r1, r2); + assert.equal(typeof r1.win, 'boolean'); + assert.ok(r1.turns >= 1); +}); + +test('simulateCombat: 약한 적이면 대체로 승리', () => { + let wins = 0; + for (let i = 0; i < 50; i++) if (simulateCombat(DATA, m32(i + 1)).win) wins++; + assert.ok(wins >= 40, `예상 승리 다수, 실제 ${wins}/50`); +}); +``` + +- [ ] **Step 2: 테스트 실패 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: FAIL (`simulateCombat is not a function`) + +- [ ] **Step 3: 구현 추가 (sim-balance.mjs)** + +```js +function bump(s, cost, dmg, blk) { + s = s || { plays: 0, energy: 0, damage: 0, block: 0 }; + s.plays++; s.energy += cost; s.damage += dmg; s.block += blk; + return s; +} + +// 단일 전투 시뮬. stats(선택): {cardId: {plays,energy,damage,block}} 누적. +// 반환: { win, turns, playerHpRemaining, draw? } +export function simulateCombat(data, rng, stats) { + const { cards, starterDeck, enemy } = data; + let drawPile = shuffle(starterDeck, rng); + let discard = []; + let hand = []; + let pHp = PLAYER_HP, pBlock = 0; + let eHp = enemy.maxHp, eBlock = 0, intentIdx = 0; + let turns = 0; + + function draw(n) { + for (let k = 0; k < n; k++) { + if (drawPile.length === 0) { drawPile = shuffle(discard, rng); discard = []; } + if (drawPile.length === 0) break; + hand.push(drawPile.pop()); + } + } + + while (turns < MAX_TURNS) { + turns++; + let energy = ENERGY; pBlock = 0; hand = []; draw(HAND_SIZE); + while (true) { + const intent = enemy.intents[intentIdx]; + const idx = chooseAction(hand, cards, energy, eHp, eBlock, intent); + if (idx < 0) break; + const id = hand[idx], c = cards[id]; + energy -= c.cost; + if (c.kind === 'Attack') { + const r = applyDamage(eHp, eBlock, c.damage || 0); eHp = r.hp; eBlock = r.block; + if (stats) stats[id] = bump(stats[id], c.cost, c.damage || 0, 0); + } else { + pBlock += c.block || 0; + if (stats) stats[id] = bump(stats[id], c.cost, 0, c.block || 0); + } + hand.splice(idx, 1); discard.push(id); + if (eHp <= 0) return { win: true, turns, playerHpRemaining: pHp }; + } + discard.push(...hand); hand = []; + eBlock = 0; + const intent = enemy.intents[intentIdx]; + if (intent.kind === 'Attack') { const r = applyDamage(pHp, pBlock, intent.value); pHp = r.hp; pBlock = r.block; } + else if (intent.kind === 'Defend') { eBlock += intent.value; } + intentIdx = (intentIdx + 1) % enemy.intents.length; + if (pHp <= 0) return { win: false, turns, playerHpRemaining: 0 }; + } + return { win: false, turns, playerHpRemaining: pHp, draw: true }; +} +``` + +- [ ] **Step 4: 테스트 통과 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: PASS (8 tests) + +- [ ] **Step 5: 커밋** + +```bash +git add tools/sim-balance.mjs tools/sim-balance.test.mjs +git commit -m "sim-balance(F): 단일 전투 시뮬 엔진 simulateCombat + 테스트" +``` + +--- + +### Task 4: runBatch·리포트·OP 탐지·CLI + +**Files:** +- Modify: `tools/sim-balance.mjs`, `tools/sim-balance.test.mjs` + +- [ ] **Step 1: 테스트 추가** + +```js +import { runBatch } from './sim-balance.mjs'; + +test('runBatch: 집계 필드·승률 범위', () => { + const r = runBatch(100, 1); + assert.equal(r.N, 100); + assert.ok(r.winRate >= 0 && r.winRate <= 1); + assert.ok(r.avgTurns > 0); + assert.ok(r.cardStats.Strike.plays > 0); +}); + +test('runBatch: 동일 시드 동일 결과', () => { + assert.deepEqual(runBatch(100, 7), runBatch(100, 7)); +}); +``` + +- [ ] **Step 2: 테스트 실패 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: FAIL (`runBatch is not a function`) + +- [ ] **Step 3: 구현 추가 (sim-balance.mjs)** + +```js +function mean(a) { return a.length ? a.reduce((s, x) => s + x, 0) / a.length : 0; } +function median(a) { + if (!a.length) return 0; + const s = a.slice().sort((x, y) => x - y), m = Math.floor(s.length / 2); + return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; +} + +export function runBatch(N, seed) { + const data = loadData(); + const rng = mulberry32(seed); + const cardStats = {}; + let wins = 0, draws = 0; + const turnsArr = [], hpArr = []; + for (let i = 0; i < N; i++) { + const r = simulateCombat(data, rng, cardStats); + if (r.draw) draws++; + if (r.win) { wins++; hpArr.push(r.playerHpRemaining); } + turnsArr.push(r.turns); + } + return { + N, wins, draws, losses: N - wins - draws, + winRate: wins / N, + avgTurns: mean(turnsArr), medianTurns: median(turnsArr), + avgHpOnWin: mean(hpArr), + cardStats, cards: data.cards, enemy: data.enemy, seed, + }; +} + +export function formatReport(r) { + const L = []; + L.push(`=== 밸런스 시뮬레이션 (적: ${r.enemy.name} HP ${r.enemy.maxHp}) ===`); + L.push(`시뮬 ${r.N}회 (seed=${r.seed})`); + L.push(`승률: ${(r.winRate * 100).toFixed(1)}% (승 ${r.wins} / 패 ${r.losses}${r.draws ? ` / 무 ${r.draws}` : ''})`); + L.push(`평균 턴: ${r.avgTurns.toFixed(2)} 중앙값 턴: ${r.medianTurns}`); + L.push(`승리 시 평균 잔여 HP: ${r.avgHpOnWin.toFixed(1)} / ${PLAYER_HP}`); + if (r.draws) L.push(`⚠️ 무승부 ${r.draws}건 (턴 상한 ${MAX_TURNS} 초과)`); + L.push(''); + L.push('카드별:'); + // 효율 계산 + kind별 중앙값으로 OP 플래그 + const rows = Object.entries(r.cardStats).map(([id, s]) => { + const kind = r.cards[id].kind; + const eff = kind === 'Attack' ? s.damage / s.energy : s.block / s.energy; + return { id, name: r.cards[id].name, kind, plays: s.plays, eff }; + }); + for (const kind of ['Attack', 'Skill']) { + const kr = rows.filter((x) => x.kind === kind); + if (!kr.length) continue; + const med = median(kr.map((x) => x.eff)); + for (const x of kr) { + const op = med > 0 && x.eff >= med * 1.5 ? ' ⚠️ OP 의심' : ''; + const unit = kind === 'Attack' ? '뎀/E' : '블록/E'; + L.push(` ${x.name}(${id2(x.id)}): 사용 ${x.plays}, 효율 ${x.eff.toFixed(2)} ${unit}${op}`); + } + } + const sorted = rows.slice().sort((a, b) => b.plays - a.plays); + if (sorted.length) L.push(`최다 사용: ${sorted[0].name} / 최소 사용: ${sorted[sorted.length - 1].name}`); + return L.join('\n'); +} +function id2(id) { return id; } + +function main() { + const args = process.argv.slice(2); + let N = 2000, seed = 1; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--seed') seed = parseInt(args[++i], 10); + else if (/^\d+$/.test(args[i])) N = parseInt(args[i], 10); + } + console.log(formatReport(runBatch(N, seed))); +} + +if (process.argv[1] && process.argv[1].endsWith('sim-balance.mjs')) main(); +``` + +- [ ] **Step 4: 테스트 통과 확인** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: PASS (10 tests) + +- [ ] **Step 5: 커밋** + +```bash +git add tools/sim-balance.mjs tools/sim-balance.test.mjs +git commit -m "sim-balance(F): runBatch·리포트·OP 탐지·CLI + 테스트" +``` + +--- + +### Task 5: 검증 (CLI 실행·결정성·데이터 반영) + +**Files:** 없음(실행 검증) + +- [ ] **Step 1: 전체 테스트** + +Run: `node --test tools/sim-balance.test.mjs` +Expected: PASS (10 tests, 0 fail) + +- [ ] **Step 2: CLI 실행 (기본)** + +Run: `node tools/sim-balance.mjs 2000` +Expected: 승률·평균턴·승리시 잔여HP·카드별 효율 리포트 출력. + +- [ ] **Step 3: 결정성 (동일 시드 동일 출력)** + +Run: `node tools/sim-balance.mjs 500 --seed 3 > /tmp/r1.txt && node tools/sim-balance.mjs 500 --seed 3 > /tmp/r2.txt && diff /tmp/r1.txt /tmp/r2.txt && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: 데이터 반영 (강타 데미지↑ → 승률·턴 변동)** + +Run: `node tools/sim-balance.mjs 1000 --seed 1 | grep 승률` (기준값 기록) → `data/cards.json`에서 Bash.damage 10→20으로 임시 변경 → `node tools/sim-balance.mjs 1000 --seed 1 | grep 승률`(변동 확인) → `git checkout -- data/cards.json`(원복). +Expected: 두 승률/턴 수치가 다름(데이터 반영). 원복 후 기준 복귀. + +- [ ] **Step 5: 최종 커밋(있다면 없음 — 검증 전용)** + +검증 전용 태스크. 변경 없음. `git status`로 `data/cards.json` 원복 확인. + +--- + +## Self-Review + +- **Spec coverage:** PRNG·applyDamage·loadData(Task1), 정책(Task2), 엔진(Task3), 집계·리포트·OP·CLI(Task4), 검증·데이터반영(Task5). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. 동기화 주석은 의도된 문서. +- **Type consistency:** `mulberry32/shuffle/applyDamage/loadData/chooseAction/simulateCombat/runBatch/formatReport` 시그니처가 정의(Task1·2·3·4)와 사용(테스트·CLI)에서 일치. `cardStats` 형태 `{plays,energy,damage,block}`가 `bump`·`runBatch`·`formatReport`에서 일치. 카드 필드 `kind/damage/block/cost`가 데이터·정책·엔진에서 일치. diff --git a/docs/superpowers/plans/2026-06-09-data-externalization.md b/docs/superpowers/plans/2026-06-09-data-externalization.md new file mode 100644 index 0000000..0ba00cf --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-data-externalization.md @@ -0,0 +1,341 @@ +# 카드/적 데이터 외부화 (TODO D) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 카드·적 데이터를 `data/cards.json`·`data/enemies.json`로 분리하고, `gen-slaydeck.mjs`가 읽어 codeblock·UI에 주입한다(데이터만 바꿔 재생성하면 반영). + +**Architecture:** 신규 JSON 2개가 데이터 단일 소스. 생성기는 상단에서 JSON을 로드·검증하고, Lua 직렬화 헬퍼로 `self.Cards`/`self.DrawPile`/적 상태를 만들어 `StartCombat`에 주입한다. DeckHud 카드 미리보기·CombatHud 초기 텍스트도 동일 데이터에서 파생. + +**Tech Stack:** Node.js ESM 생성기, JSON 데이터, MSW Lua codeblock/UI JSON. 검증은 `node --check`+재생성+sha1 결정성+데이터변경 반영 확인+메이커 Play. + +--- + +## File Structure + +- Create: `data/cards.json` — 카드 정의(`cards`) + 시작 덱(`starterDeck`). +- Create: `data/enemies.json` — 적 정의(`enemies`) + 활성 적(`activeEnemy`). +- Modify: `tools/gen-slaydeck.mjs` — JSON 로드·검증·Lua 직렬화 헬퍼, `StartCombat`/`upsertUi`/속성 데이터화. + +검증 한계: MSW Lua 단위 테스트 러너 없음 → 자동 검증은 생성기 문법·재생성·결정성·데이터 반영·JSON 유효성. 실제 동작은 메이커 Play(사용자). + +--- + +### Task 1: 데이터 파일 생성 + +**Files:** +- Create: `data/cards.json` +- Create: `data/enemies.json` + +- [ ] **Step 1: `data/cards.json` 작성** + +```json +{ + "cards": { + "Strike": { "name": "타격", "cost": 1, "kind": "Attack", "damage": 6, "desc": "피해 6" }, + "Defend": { "name": "방어", "cost": 1, "kind": "Skill", "block": 5, "desc": "방어도 5" }, + "Bash": { "name": "강타", "cost": 2, "kind": "Attack", "damage": 10, "desc": "피해 10" } + }, + "starterDeck": ["Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash"] +} +``` + +- [ ] **Step 2: `data/enemies.json` 작성** + +```json +{ + "enemies": { + "slime": { + "name": "슬라임", + "maxHp": 45, + "intents": [ + { "kind": "Attack", "value": 10 }, + { "kind": "Attack", "value": 6 }, + { "kind": "Defend", "value": 8 } + ] + } + }, + "activeEnemy": "slime" +} +``` + +- [ ] **Step 3: JSON 유효성 확인** + +Run: `node -e "JSON.parse(require('fs').readFileSync('data/cards.json','utf8')); JSON.parse(require('fs').readFileSync('data/enemies.json','utf8')); console.log('JSON OK')"` +Expected: `JSON OK` + +- [ ] **Step 4: 커밋** + +```bash +git add data/cards.json data/enemies.json +git commit -m "data(D): 카드/적 데이터 JSON 외부화 파일 추가" +``` + +--- + +### Task 2: 생성기에 JSON 로드·검증·Lua 직렬화 헬퍼 추가 + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (상단 import 직후) + +- [ ] **Step 1: 파일 상단 `import { readFileSync, writeFileSync } from 'node:fs';` 바로 다음에 추가** + +```js +const CARDS = JSON.parse(readFileSync('data/cards.json', 'utf8')); +const ENEMIES = JSON.parse(readFileSync('data/enemies.json', 'utf8')); + +// 검증 (fail-fast): 잘못된 데이터면 생성 중단 +for (const id of CARDS.starterDeck) { + if (!CARDS.cards[id]) { + throw new Error(`[gen-slaydeck] starterDeck에 없는 카드 id 참조: ${id}`); + } +} +if (!ENEMIES.enemies[ENEMIES.activeEnemy]) { + throw new Error(`[gen-slaydeck] activeEnemy가 enemies에 없음: ${ENEMIES.activeEnemy}`); +} +const ACTIVE_ENEMY = ENEMIES.enemies[ENEMIES.activeEnemy]; + +// Lua 직렬화 헬퍼 +function luaStr(s) { + return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; +} +function luaCardsTable(cards) { + const lines = Object.entries(cards).map(([id, c]) => { + const fields = [`name = ${luaStr(c.name)}`, `cost = ${c.cost}`, `desc = ${luaStr(c.desc)}`, `kind = ${luaStr(c.kind)}`]; + if (c.damage != null) fields.push(`damage = ${c.damage}`); + if (c.block != null) fields.push(`block = ${c.block}`); + return `\t${id} = { ${fields.join(', ')} },`; + }); + return `self.Cards = {\n${lines.join('\n')}\n}`; +} +function luaDeckTable(deck) { + return `self.DrawPile = { ${deck.map(luaStr).join(', ')} }`; +} +function luaIntentsTable(intents) { + const lines = intents.map((it) => `\t{ kind = ${luaStr(it.kind)}, value = ${it.value} },`); + return `self.EnemyIntents = {\n${lines.join('\n')}\n}`; +} +function intentText(it) { + if (it.kind === 'Attack') return `의도: 공격 ${it.value}`; + if (it.kind === 'Defend') return `의도: 방어 ${it.value}`; + return ''; +} +``` + +- [ ] **Step 2: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 3: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(D): JSON 로드·검증·Lua 직렬화 헬퍼 추가" +``` + +--- + +### Task 3: StartCombat·EnemyMaxHp 속성을 데이터에서 생성 + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`prop('number', 'EnemyMaxHp', ...)`, `method('StartCombat', ...)`) + +- [ ] **Step 1: EnemyMaxHp 속성 기본값을 데이터로** + +`prop('number', 'EnemyMaxHp', '45'),` 를 아래로 교체: + +```js + prop('number', 'EnemyMaxHp', String(ACTIVE_ENEMY.maxHp)), +``` + +- [ ] **Step 2: `StartCombat` 메서드 본문을 데이터 주입형으로 교체** + +기존 `method('StartCombat', \`...\`)` 호출 전체(아래 "현재" 블록)를 "신규"로 교체. + +현재(교체 대상): +``` +self.MaxEnergy = 3 +self.Turn = 0 +self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.PlayerBlock = 0 +self.EnemyName = "슬라임" +self.EnemyMaxHp = 45 +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = { + { kind = "Attack", value = 10 }, + { kind = "Attack", value = 6 }, + { kind = "Defend", value = 8 }, +} +self.EnemyIntentIndex = 1 +self.CombatOver = false +self.DiscardPile = {} +self.Hand = {} +self.Cards = { + Strike = { name = "타격", cost = 1, desc = "피해 6", kind = "Attack", damage = 6 }, + Defend = { name = "방어", cost = 1, desc = "방어도 5", kind = "Skill", block = 5 }, + Bash = { name = "강타", cost = 2, desc = "피해 10", kind = "Attack", damage = 10 }, +} +self.DrawPile = { "Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash" } +self:Shuffle(self.DrawPile) +self:BindButtons() +self:RenderCombat() +self:StartPlayerTurn() +``` + +신규 — `method('StartCombat', ...)`의 코드 인자를 템플릿으로 생성: +```js + method('StartCombat', `self.MaxEnergy = 3 +self.Turn = 0 +self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.PlayerBlock = 0 +self.EnemyName = ${luaStr(ACTIVE_ENEMY.name)} +self.EnemyMaxHp = ${ACTIVE_ENEMY.maxHp} +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +${luaIntentsTable(ACTIVE_ENEMY.intents)} +self.EnemyIntentIndex = 1 +self.CombatOver = false +self.DiscardPile = {} +self.Hand = {} +${luaCardsTable(CARDS.cards)} +${luaDeckTable(CARDS.starterDeck)} +self:Shuffle(self.DrawPile) +self:BindButtons() +self:RenderCombat() +self:StartPlayerTurn()`), +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(D): StartCombat·EnemyMaxHp를 데이터에서 생성" +``` + +--- + +### Task 4: DeckHud 카드 미리보기·CombatHud 초기 텍스트를 데이터에서 파생 + +**Files:** +- Modify: `tools/gen-slaydeck.mjs` (`upsertUi`의 `cards` 배열, `enemyTexts` 초기값) + +- [ ] **Step 1: `upsertUi`의 카드 미리보기 배열을 데이터 파생으로 교체** + +기존: +```js + const cards = [ + { name: '타격', cost: '1', desc: '피해 6', tint: ATTACK }, + { name: '타격', cost: '1', desc: '피해 6', tint: ATTACK }, + { name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND }, + { name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND }, + { name: '강타', cost: '2', desc: '피해 10', tint: ATTACK }, + ]; +``` +교체: +```js + const cards = CARDS.starterDeck.slice(0, 5).map((id) => { + const c = CARDS.cards[id]; + return { name: c.name, cost: String(c.cost), desc: c.desc, tint: c.kind === 'Attack' ? ATTACK : DEFEND }; + }); +``` + +- [ ] **Step 2: CombatHud `enemyTexts` 초기값을 데이터에서 파생** + +기존: +```js + const enemyTexts = [ + ['EnemyName', { x: 0, y: 358 }, { x: 360, y: 44 }, '슬라임', 28, true, GOLD], + ['EnemyHp', { x: 0, y: 316 }, { x: 360, y: 40 }, 'HP 45/45', 24, true, { r: 1, g: 1, b: 1, a: 1 }], + ['EnemyBlock', { x: 0, y: 280 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }], + ['EnemyIntent', { x: 0, y: 244 }, { x: 360, y: 38 }, '의도: 공격 10', 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }], + ]; +``` +교체: +```js + const enemyTexts = [ + ['EnemyName', { x: 0, y: 358 }, { x: 360, y: 44 }, ACTIVE_ENEMY.name, 28, true, GOLD], + ['EnemyHp', { x: 0, y: 316 }, { x: 360, y: 40 }, `HP ${ACTIVE_ENEMY.maxHp}/${ACTIVE_ENEMY.maxHp}`, 24, true, { r: 1, g: 1, b: 1, a: 1 }], + ['EnemyBlock', { x: 0, y: 280 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }], + ['EnemyIntent', { x: 0, y: 244 }, { x: 360, y: 38 }, intentText(ACTIVE_ENEMY.intents[0]), 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }], + ]; +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(D): 카드 미리보기·CombatHud 초기 텍스트 데이터 파생" +``` + +--- + +### Task 5: 재생성 + 검증 + +**Files:** 생성물 3종 (생성기 실행 결과) + +- [ ] **Step 1: 생성기 실행** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 생성물이 B와 동치인지 — codeblock에 데이터 값이 반영됐는지 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/Strike = { name = \"타격\".*damage = 6/.test(sc) && /슬라임/.test(sc) && /value = 10/.test(sc) ? 'DATA INJECTED OK' : 'MISMATCH')"` +Expected: `DATA INJECTED OK` + +- [ ] **Step 3: 결정성 확인** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: 데이터 변경이 반영되는지 확인 (D의 핵심 검증)** + +Run: `node -e "const fs=require('fs'); const f='data/cards.json'; const o=JSON.parse(fs.readFileSync(f,'utf8')); o.cards.Strike.damage=9; o.cards.Strike.desc='피해 9'; fs.writeFileSync(f, JSON.stringify(o,null,2));" && node tools/gen-slaydeck.mjs >/dev/null && node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/Strike = { name = \"타격\", cost = 1, desc = \"피해 9\", kind = \"Attack\", damage = 9/.test(sc) ? 'CHANGE REFLECTED' : 'NOT REFLECTED')"` +Expected: `CHANGE REFLECTED` + +- [ ] **Step 5: 변경 되돌리고 재생성 (원복)** + +Run: `git checkout -- data/cards.json && node tools/gen-slaydeck.mjs >/dev/null && echo reverted` +Expected: `reverted` + +- [ ] **Step 6: 잘못된 데이터 fail-fast 확인** + +Run: `node -e "const fs=require('fs'); const o=JSON.parse(fs.readFileSync('data/enemies.json','utf8')); o.activeEnemy='nope'; fs.writeFileSync('/tmp/bad-enemies.json', JSON.stringify(o));" && cp data/enemies.json /tmp/enemies.bak && cp /tmp/bad-enemies.json data/enemies.json; node tools/gen-slaydeck.mjs; echo "exit=$?"; cp /tmp/enemies.bak data/enemies.json` +Expected: 에러 메시지 `activeEnemy가 enemies에 없음: nope` + `exit=1`, 이후 원복 + +- [ ] **Step 7: 최종 재생성 + git status 확인** + +Run: `node tools/gen-slaydeck.mjs >/dev/null; git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `data/*.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock`만 변경(내용 동일한 common 제외). + +- [ ] **Step 8: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(D): 데이터 기반 카드/적 주입 반영" +``` + +- [ ] **Step 9: 메이커 Play 수동 검증 (사용자)** + +메이커 reload→Play: 기존 B 동작과 동일(데이터 동치라 회귀 없음). 적 슬라임 HP 45·의도 공격10, 카드 3종 효과 정상. + +--- + +## Self-Review + +- **Spec coverage:** cards.json/enemies.json 생성(Task1), 로드·검증·직렬화(Task2), StartCombat·속성 데이터화(Task3), UI 파생(Task4), 검증·데이터변경 반영(Task5). 스펙 전 항목 매핑됨. +- **Placeholder scan:** 모든 단계 실제 코드/명령 포함. "TODO(E)"류 미래 훅은 본 작업 범위 아님. +- **Type consistency:** `luaStr`/`luaCardsTable`/`luaDeckTable`/`luaIntentsTable`/`intentText`/`ACTIVE_ENEMY`/`CARDS`/`ENEMIES` 명칭이 정의부(Task2)와 사용부(Task3·4)에서 일치. 카드 필드(`name/cost/kind/damage/block/desc`)가 데이터(Task1)·직렬화(Task2)·검증(Task5)에서 일치. diff --git a/docs/superpowers/plans/2026-06-09-floors.md b/docs/superpowers/plans/2026-06-09-floors.md new file mode 100644 index 0000000..5cb36e8 --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-floors.md @@ -0,0 +1,206 @@ +# 다음 층 / 멀티 act (TODO E6a) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 보스 클리어 시 즉시 종료 대신 다음 막으로 진행(적 스케일), 최종 막 보스에서 진짜 런 클리어. + +**Architecture:** `Floor`를 막 카운터로 재정의(1..ACT_COUNT). StartCombat에서 적을 막 배율로 스케일, CheckCombatEnd 보스 승리 시 다음 막(같은 맵 재사용)으로. 모두 `gen-slaydeck.mjs`에서 생성. + +**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock. 검증은 node --check+재생성+결정성+메이커 Play. + +--- + +## File Structure +- Modify: `tools/gen-slaydeck.mjs` — ACT_COUNT 상수, StartRun(Floor=1·RunLength=ACT_COUNT), StartCombat(Floor 제거·적 스케일), CheckCombatEnd(보스 다음 막), RenderRun(막 라벨). + +검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play. + +--- + +### Task 1: ACT_COUNT 상수 + StartRun + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: ACT_COUNT 상수** — `const RELIC_PRICE = 60;` 다음에: + +```js + const ACT_COUNT = 3; +``` + +- [ ] **Step 2: StartRun의 Floor·RunLength 변경** — StartRun 코드에서 아래 두 줄을 교체: + +기존: +``` +self.Floor = 0 +self.RunLength = ${MAX_ROW} +``` +신규: +``` +self.Floor = 1 +self.RunLength = ${ACT_COUNT} +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E6a): ACT_COUNT·StartRun 막 카운터 초기화" +``` + +--- + +### Task 2: StartCombat — Floor 제거 + 적 막 스케일 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: Floor=node.row 블록 제거 + 적 스케일 적용** — StartCombat 코드의 아래 블록을 교체: + +기존: +``` +local node = self.MapNodes[self.CurrentNodeId] +if node ~= nil then + self.Floor = node.row +end +local enemy = self.Enemies[self.CurrentEnemyId] +self.PlayerBlock = 0 +self.EnemyName = enemy.name +self.EnemyMaxHp = enemy.maxHp +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = enemy.intents +self.EnemyIntentIndex = 1 +``` +신규: +``` +local enemy = self.Enemies[self.CurrentEnemyId] +local mult = 1 + (self.Floor - 1) * 0.6 +self.PlayerBlock = 0 +self.EnemyName = enemy.name +self.EnemyMaxHp = math.floor(enemy.maxHp * mult) +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = {} +for i = 1, #enemy.intents do + self.EnemyIntents[i] = { kind = enemy.intents[i].kind, value = math.floor(enemy.intents[i].value * mult) } +end +self.EnemyIntentIndex = 1 +``` + +- [ ] **Step 2: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 3: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E6a): StartCombat 적 막 스케일·Floor 제거" +``` + +--- + +### Task 3: CheckCombatEnd 보스 다음 막 + RenderRun 막 라벨 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: CheckCombatEnd 보스 분기 교체** — 아래 블록을 교체: + +기존: +``` + if node ~= nil and node.type == "boss" then + self:ShowResult("런 클리어!") + self.RunActive = false + else + self:OfferReward() + end +``` +신규: +``` + if node ~= nil and node.type == "boss" then + if self.Floor < self.RunLength then + self.Floor = self.Floor + 1 + self.CurrentNodeId = "" + self.CurrentEnemyId = "" + self:RenderRun() + self:ShowMap() + else + self:ShowResult("런 클리어!") + self.RunActive = false + end + else + self:OfferReward() + end +``` + +- [ ] **Step 2: RenderRun 막 라벨** — RenderRun의 Floor 텍스트 줄을 교체: + +기존: +``` +self:SetText("/ui/DefaultGroup/CombatHud/Floor", "층 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength)) +``` +신규: +``` +self:SetText("/ui/DefaultGroup/CombatHud/Floor", "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength)) +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E6a): 보스 승리 다음 막 진행·막 라벨" +``` + +--- + +### Task 4: 재생성 + 검증 + +**Files:** 생성물 + +- [ ] **Step 1: 생성** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 스케일·막 진행 코드 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartCombat').Code; console.log(/mult = 1 \+ \(self.Floor - 1\) \* 0.6/.test(sc)&&/math.floor\(enemy.maxHp \* mult\)/.test(sc)?'SCALE OK':'NO SCALE'); const cc=j.ContentProto.Json.Methods.find(m=>m.Name==='CheckCombatEnd').Code; console.log(/self.Floor = self.Floor \+ 1/.test(cc)?'NEXT-ACT OK':'NO NEXT-ACT')"` +Expected: `SCALE OK` / `NEXT-ACT OK` + +- [ ] **Step 3: 결정성** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: git status** + +Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). (data 변경 없음) + +- [ ] **Step 5: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(E6a): 멀티 act·적 스케일 반영" +``` + +- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)** + +reload→Play: 1막 보스(슬라임 킹 120) 처치 → 2막 맵(Floor 2)·적 HP 스케일(슬라임 72·보스 192) → 3막 보스 처치 → "런 클리어!". HP/골드/덱/유물 막 간 유지. MCP는 PickNode/PlayCard/CheckCombatEnd 직접 호출 + 로그. + +--- + +## Self-Review +- **Spec coverage:** ACT_COUNT·StartRun(Task1), StartCombat 스케일·Floor제거(Task2), 보스 다음막·막라벨(Task3), 검증(Task4). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. +- **Type consistency:** `Floor`(막 카운터)·`RunLength`(=ACT_COUNT)·`mult` 사용 일관. `EnemyIntents` 새 테이블 생성(공유 변형 없음). CheckCombatEnd의 `node`는 기존 정의 사용. ACT_COUNT 상수 Task1 정의·Task1·3 사용. diff --git a/docs/superpowers/plans/2026-06-09-map-nodes.md b/docs/superpowers/plans/2026-06-09-map-nodes.md new file mode 100644 index 0000000..8cf1377 --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-map-nodes.md @@ -0,0 +1,493 @@ +# 분기 맵 노드 진행 (TODO E3) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 플레이어가 작성된 분기 맵(DAG)에서 다음 노드를 선택해 전투/엘리트/보스로 진행, 보스 클리어 시 "런 클리어". + +**Architecture:** `data/map.json`(그래프)·`data/enemies.json`(다중 적)을 `gen-slaydeck.mjs`가 로드·주입. SlayDeckController에 맵 상태·네비게이션 메서드 추가, MapHud UI 생성. 자동 진행 대신 ShowMap→PickNode→StartCombat→보상→ShowMap 루프. + +**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play. + +--- + +## File Structure +- Create: `data/map.json` — 분기 맵. +- Modify: `data/enemies.json` — slime_elite·slime_boss 추가. +- Modify: `tools/gen-slaydeck.mjs` — 맵/적 로드·검증·직렬화 헬퍼, method() returnType, 속성·메서드·MapHud UI. + +검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play. + +--- + +### Task 1: 데이터 + 로드·검증·직렬화 헬퍼 + +**Files:** Create `data/map.json`; Modify `data/enemies.json`, `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: `data/map.json` 작성** + +```json +{ + "start": ["A", "B"], + "nodes": { + "A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] }, + "B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["D", "E"] }, + "C": { "type": "elite", "enemy": "slime_elite", "row": 2, "col": -2, "next": ["BOSS"] }, + "D": { "type": "combat", "enemy": "slime", "row": 2, "col": 0, "next": ["BOSS"] }, + "E": { "type": "combat", "enemy": "slime", "row": 2, "col": 2, "next": ["BOSS"] }, + "BOSS": { "type": "boss", "enemy": "slime_boss", "row": 3, "col": 0, "next": [] } + } +} +``` + +- [ ] **Step 2: `data/enemies.json`에 엘리트·보스 추가** — `slime` 항목 다음에: + +```json + "slime_elite": { + "name": "정예 슬라임", + "maxHp": 70, + "intents": [ + { "kind": "Attack", "value": 14 }, + { "kind": "Attack", "value": 8 }, + { "kind": "Defend", "value": 10 } + ] + }, + "slime_boss": { + "name": "슬라임 킹", + "maxHp": 120, + "intents": [ + { "kind": "Attack", "value": 18 }, + { "kind": "Defend", "value": 12 }, + { "kind": "Attack", "value": 10 }, + { "kind": "Attack", "value": 22 } + ] + } +``` + +- [ ] **Step 3: 생성기 상단에 map 로드·검증·헬퍼 추가** — `const ACTIVE_ENEMY = ...;` 다음에: + +```js +const MAP = JSON.parse(readFileSync('data/map.json', 'utf8')); +for (const id of MAP.start) { + if (!MAP.nodes[id]) throw new Error(`[gen-slaydeck] map.start에 없는 노드 id: ${id}`); +} +for (const [id, n] of Object.entries(MAP.nodes)) { + if (!ENEMIES.enemies[n.enemy]) throw new Error(`[gen-slaydeck] 노드 ${id}의 enemy 없음: ${n.enemy}`); + for (const nx of n.next) { + if (!MAP.nodes[nx]) throw new Error(`[gen-slaydeck] 노드 ${id}.next에 없는 노드 id: ${nx}`); + } +} +const MAX_ROW = Math.max(...Object.values(MAP.nodes).map((n) => n.row)); + +function luaIntentsArray(intents) { + return '{ ' + intents.map((it) => `{ kind = ${luaStr(it.kind)}, value = ${it.value} }`).join(', ') + ' }'; +} +function luaEnemiesTable(enemies) { + const lines = Object.entries(enemies).map(([id, e]) => + `\t${id} = { name = ${luaStr(e.name)}, maxHp = ${e.maxHp}, intents = ${luaIntentsArray(e.intents)} },`); + return `self.Enemies = {\n${lines.join('\n')}\n}`; +} +function luaMapNodesTable(nodes) { + const lines = Object.entries(nodes).map(([id, n]) => { + const nx = '{ ' + n.next.map(luaStr).join(', ') + ' }'; + return `\t${id} = { type = ${luaStr(n.type)}, enemy = ${luaStr(n.enemy)}, row = ${n.row}, col = ${n.col}, next = ${nx} },`; + }); + return `self.MapNodes = {\n${lines.join('\n')}\n}`; +} +function luaStartArray(start) { + return 'self.MapStart = { ' + start.map(luaStr).join(', ') + ' }'; +} +``` + +- [ ] **Step 4: method()에 ReturnType 파라미터 추가** — 기존 method 함수를: + +```js +function method(Name, Code, Arguments = [], ExecSpace = 0, ReturnType = 'void') { + return { + Return: { Type: ReturnType, DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null }, + Arguments, + Code, + Scope: 2, + ExecSpace, + Attributes: [], + Name, + }; +} +``` + +- [ ] **Step 5: JSON·문법 검사** + +Run: `node -e "JSON.parse(require('fs').readFileSync('data/map.json','utf8')); JSON.parse(require('fs').readFileSync('data/enemies.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs` +Expected: `JSON OK` + 오류 없음 + +- [ ] **Step 6: 커밋** + +```bash +git add data/map.json data/enemies.json tools/gen-slaydeck.mjs +git commit -m "data(E3): 분기 맵 map.json·엘리트/보스 적 + 직렬화 헬퍼" +``` + +--- + +### Task 2: 맵 속성 + StartRun(맵 빌드·ShowMap) + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: 맵 상태 속성 추가** — `prop('boolean', 'RunActive', 'false'),` 다음에: + +```js + prop('any', 'Enemies'), + prop('any', 'MapNodes'), + prop('any', 'MapStart'), + prop('string', 'CurrentNodeId', '""'), + prop('string', 'CurrentEnemyId', '""'), +``` + +- [ ] **Step 2: StartRun 교체** — 맵 빌드 + ShowMap: + +```js + method('StartRun', `self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.Gold = 0 +self.Floor = 0 +self.RunLength = ${MAX_ROW} +self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} } +self.RunActive = true +${luaEnemiesTable(ENEMIES.enemies)} +${luaMapNodesTable(MAP.nodes)} +${luaStartArray(MAP.start)} +self.CurrentNodeId = "" +self.CurrentEnemyId = "" +self:BindButtons() +self:ShowMap()`), +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E3): 맵 상태 속성·StartRun 맵 빌드/ShowMap" +``` + +--- + +### Task 3: StartCombat·CheckCombatEnd·PickReward (맵 연동) + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: StartCombat 교체** — 적을 self.Enemies에서 로드, Floor=노드 row: + +```js + method('StartCombat', `self.MaxEnergy = 3 +self.Turn = 0 +local node = self.MapNodes[self.CurrentNodeId] +if node ~= nil then + self.Floor = node.row +end +local enemy = self.Enemies[self.CurrentEnemyId] +self.PlayerBlock = 0 +self.EnemyName = enemy.name +self.EnemyMaxHp = enemy.maxHp +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = enemy.intents +self.EnemyIntentIndex = 1 +self.CombatOver = false +self.DiscardPile = {} +self.Hand = {} +${luaCardsTable(CARDS.cards)} +self.DrawPile = {} +for i = 1, #self.RunDeck do + self.DrawPile[i] = self.RunDeck[i] +end +self:Shuffle(self.DrawPile) +self:RenderCombat() +self:StartPlayerTurn()`), +``` + +- [ ] **Step 2: CheckCombatEnd 교체** — 보스 노드면 런 클리어: + +```js + method('CheckCombatEnd', `if self.EnemyHp <= 0 then + self.CombatOver = true + self.Gold = self.Gold + ${GOLD_PER_WIN} + self:RenderRun() + local node = self.MapNodes[self.CurrentNodeId] + if node ~= nil and node.type == "boss" then + self:ShowResult("런 클리어!") + self.RunActive = false + else + self:OfferReward() + end +elseif self.PlayerHp <= 0 then + self.CombatOver = true + self:ShowResult("패배...") + self.RunActive = false +end`), +``` + +- [ ] **Step 3: PickReward 마지막을 ShowMap으로** — PickReward 코드의 마지막 `self:StartCombat()`를 `self:ShowMap()`로 교체. (그 외 동일) + +```js + method('PickReward', `if self.CombatOver ~= true or self.RunActive ~= true then + return +end +if slot ~= 0 and self.RewardChoices ~= nil then + local id = self.RewardChoices[slot] + if id ~= nil then + table.insert(self.RunDeck, id) + end +end +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud") +if hud ~= nil then + hud.Enable = false +end +self:ShowMap()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), +``` + +- [ ] **Step 4: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 5: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E3): StartCombat 적 데이터화·보스 런클리어·보상후 맵복귀" +``` + +--- + +### Task 4: ShowMap·IsReachable·PickNode·RenderMap + BindButtons + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: 맵 메서드 추가** — PickReward 메서드 다음(마지막 `]);` 직전)에 삽입: + +```js + method('ShowMap', `self:RenderMap() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud") +if hud ~= nil then + hud.Enable = true +end`), + method('IsReachable', `local list +if self.CurrentNodeId == "" then + list = self.MapStart +else + local node = self.MapNodes[self.CurrentNodeId] + if node == nil then + return false + end + list = node.next +end +for i = 1, #list do + if list[i] == id then + return true + end +end +return false`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }], 0, 'boolean'), + method('RenderMap', `for id, node in pairs(self.MapNodes) do + local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. id) + if e ~= nil then + local reachable = self:IsReachable(id) + if e.SpriteGUIRendererComponent ~= nil then + if reachable then + e.SpriteGUIRendererComponent.Color = Color(0.3, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + end + end + if e.ButtonComponent ~= nil then + e.ButtonComponent.Enable = reachable + end + end +end`), + method('PickNode', `if self.RunActive ~= true then + return +end +if self:IsReachable(id) ~= true then + return +end +self.CurrentNodeId = id +self.CurrentEnemyId = self.MapNodes[id].enemy +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud") +if hud ~= nil then + hud.Enable = false +end +self:StartCombat()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]), +``` + +- [ ] **Step 2: BindButtons에 맵 노드 버튼 바인딩 추가** — BindButtons 코드의 마지막 `end`(skip 바인딩) 다음에 추가. BindButtons 끝부분의 skip 블록 다음에 붙이도록, skip 블록을 아래로 교체: + +```js +local skip = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Skip") +if skip ~= nil and skip.ButtonComponent ~= nil then + skip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end) +end +local mapNodeIds = { ${Object.keys(MAP.nodes).map(luaStr).join(', ')} } +for i = 1, #mapNodeIds do + local nid = mapNodeIds[i] + local mn = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. nid) + if mn ~= nil and mn.ButtonComponent ~= nil then + mn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end) + end +end`), +``` +(BindButtons 전체에서 기존 skip 블록 `local skip = ... end`)` 부분을 위 블록으로 교체) + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E3): ShowMap/IsReachable/PickNode/RenderMap·맵 노드 바인딩" +``` + +--- + +### Task 5: MapHud UI 생성 + +**Files:** Modify `tools/gen-slaydeck.mjs` (`guid`, `upsertUi`) + +- [ ] **Step 1: guid 'map' 분기** — ns 매핑에 추가: + +```js + const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : 0xfe; +``` + +- [ ] **Step 2: 필터 확장** — upsertUi 필터에 MapHud 추가: + +```js + ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud')); +``` + +- [ ] **Step 3: MapHud 그룹 생성** — `ui.ContentProto.Entities.push(...reward);` 다음에 삽입: + +```js + const TYPE_KO = { combat: '전투', elite: '엘리트', boss: '보스' }; + const map = []; + const mapHud = entity({ + id: guid('map', 0), + path: '/ui/DefaultGroup/MapHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 7, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.9 }, type: 1, raycast: true }), + ], + }); + mapHud.jsonString.enable = false; + map.push(mapHud); + map.push(entity({ + id: guid('map', 1), + path: '/ui/DefaultGroup/MapHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 510 } }), + sprite({ color: TRANSPARENT }), + text({ value: '다음 노드 선택', fontSize: 40, bold: true, color: GOLD, alignment: 4 }), + ], + })); + let mapN = 2; + for (const [id, node] of Object.entries(MAP.nodes)) { + const nodePath = `/ui/DefaultGroup/MapHud/Node_${id}`; + const pos = { x: node.col * 180, y: node.row * 170 - 80 }; + map.push(entity({ + id: guid('map', mapN++), + path: nodePath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: node.row, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 150, y: 80 }, pos }), + sprite({ color: { r: 0.3, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }), + button(), + ], + })); + map.push(entity({ + id: guid('map', mapN++), + path: `${nodePath}/Label`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 150, parentH: 80, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 144, y: 72 }, pos: { x: 0, y: 0 } }), + sprite({ color: TRANSPARENT }), + text({ value: `${TYPE_KO[node.type]}\n${ENEMIES.enemies[node.enemy].name}`, fontSize: 20, bold: true }), + ], + })); + } + ui.ContentProto.Entities.push(...map); +``` + +- [ ] **Step 4: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 5: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E3): MapHud 노드 맵 UI 생성" +``` + +--- + +### Task 6: 재생성 + 검증 + +**Files:** 생성물 + +- [ ] **Step 1: 생성** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 메서드·UI·적 주입 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ShowMap','PickNode','IsReachable','RenderMap'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const sc=j.ContentProto.Json.Methods.find(m=>m.Name==='StartRun').Code; console.log(/slime_boss/.test(sc)&&/슬라임 킹/.test(sc)?'ENEMIES OK':'NO ENEMIES'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/MapHud')&&has('/ui/DefaultGroup/MapHud/Node_BOSS')&&has('/ui/DefaultGroup/MapHud/Node_A/Label')?'UI OK':'UI MISSING')"` +Expected: `METHODS OK` / `ENEMIES OK` / `UI OK` + +- [ ] **Step 3: 결정성** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: git status** + +Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `data/*`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). + +- [ ] **Step 5: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(E3): 분기 맵·다중 적 반영" +``` + +- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)** + +reload→Play: StartRun → MapHud(A·B만 클릭 가능) → PickNode("A") → 슬라임 전투 → 승리 → 보상 → 맵(C·D 활성) → PickNode("C") → 정예 슬라임(HP70) → ... → BOSS → 슬라임킹(HP120) → 승리 → "런 클리어!". 도달 불가 노드 PickNode → 무시. MCP는 `PickNode`/`PlayCard`/`PickReward` 직접 호출 + 상태 로그로 검증. + +--- + +## Self-Review +- **Spec coverage:** map.json/적(Task1), 맵 상태·StartRun(Task2), StartCombat 적데이터·보스클리어·보상후맵(Task3), Show/Pick/Reachable/RenderMap·바인딩(Task4), MapHud UI(Task5), 검증(Task6). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. +- **Type consistency:** 메서드 `StartRun/ShowMap/IsReachable/PickNode/RenderMap/StartCombat/CheckCombatEnd/PickReward` 정의·호출 일치. 속성 `Enemies/MapNodes/MapStart/CurrentNodeId/CurrentEnemyId` 정의(Task2)·사용(Task3·4) 일치. UI 경로 `/ui/DefaultGroup/MapHud/Node_{id}`·`/Label`가 codeblock(RenderMap/PickNode/BindButtons)·생성(Task5)에서 동일(노드 id는 map.json 키). `IsReachable`는 boolean 반환(method returnType param, Task1). enemy 필드 `name/maxHp/intents`가 데이터·luaEnemiesTable·StartCombat에서 일치. diff --git a/docs/superpowers/plans/2026-06-09-relics.md b/docs/superpowers/plans/2026-06-09-relics.md new file mode 100644 index 0000000..4513ac0 --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-relics.md @@ -0,0 +1,400 @@ +# 유물 (TODO E5) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 훅 기반 유물 패시브 + 3획득 경로(시작/엘리트/상점)를 추가한다. + +**Architecture:** `data/relics.json`을 생성기가 주입(self.Relics). `ApplyRelics(hook)`을 전투시작/턴시작/카드사용/보상 4지점에서 호출. `AddRelic`을 3경로가 공유. ShopHud 유물 슬롯·상단 유물 바 UI. 모두 `gen-slaydeck.mjs`에서 생성. + +**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play. + +--- + +## File Structure +- Create: `data/relics.json`. +- Modify: `tools/gen-slaydeck.mjs` — 로드/검증/직렬화, 상수, 속성, 훅 메서드(ApplyRelics/AddRelic/RenderRelics), 4지점 통합, 상점 유물(BuyRelic), UI(유물 바·상점 유물 슬롯). + +검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play. + +--- + +### Task 1: 데이터 + 로드/직렬화 + 상수/속성 + 훅 메서드 + +**Files:** Create `data/relics.json`; Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: `data/relics.json` 작성** + +```json +{ + "relics": { + "ironHeart": { "name": "강철 심장", "desc": "전투 시작 시 방어도 +6", "hook": "combatStart", "effect": "block", "value": 6 }, + "energyCore": { "name": "에너지 코어", "desc": "턴 시작 시 에너지 +1", "hook": "turnStart", "effect": "energy", "value": 1 }, + "vampire": { "name": "흡혈 송곳니", "desc": "공격 카드 사용 시 HP +1", "hook": "cardPlayed", "effect": "healOnAttack", "value": 1 }, + "goldIdol": { "name": "황금 우상", "desc": "전투 승리 시 골드 +10", "hook": "combatReward", "effect": "gold", "value": 10 } + }, + "startingRelic": "ironHeart", + "relicPool": ["energyCore", "vampire", "goldIdol"] +} +``` + +- [ ] **Step 2: 로드·검증·직렬화 헬퍼** — `const MAP = ...` 로드 블록 다음(MAX_ROW 정의 뒤)에 추가: + +```js +const RELICS = JSON.parse(readFileSync('data/relics.json', 'utf8')); +if (!RELICS.relics[RELICS.startingRelic]) throw new Error(`[gen-slaydeck] startingRelic 없음: ${RELICS.startingRelic}`); +for (const id of RELICS.relicPool) { + if (!RELICS.relics[id]) throw new Error(`[gen-slaydeck] relicPool에 없는 유물 id: ${id}`); +} +function luaRelicsTable(relics) { + const lines = Object.entries(relics).map(([id, r]) => + `\t${id} = { name = ${luaStr(r.name)}, desc = ${luaStr(r.desc)}, hook = ${luaStr(r.hook)}, effect = ${luaStr(r.effect)}, value = ${r.value} },`); + return `self.Relics = {\n${lines.join('\n')}\n}`; +} +``` + +- [ ] **Step 3: RELIC_PRICE 상수** — `const REST_HEAL = 30;` 다음에: + +```js + const RELIC_PRICE = 60; +``` + +- [ ] **Step 4: 속성 추가** — `prop('any', 'ShopBought'),` 다음에: + +```js + prop('any', 'Relics'), + prop('any', 'RunRelics'), + prop('any', 'RelicPool'), + prop('string', 'ShopRelic', '""'), + prop('boolean', 'ShopRelicBought', 'false'), +``` + +- [ ] **Step 5: 훅 메서드 추가** — PickReward 메서드 다음(ShowMap 앞 아무 곳, 마지막 `]);` 전 임의 위치)에 삽입: + +```js + method('ApplyRelics', `if self.RunRelics == nil then + return +end +for i = 1, #self.RunRelics do + local r = self.Relics[self.RunRelics[i]] + if r ~= nil and r.hook == hook then + if r.effect == "block" then + self.PlayerBlock = self.PlayerBlock + r.value + elseif r.effect == "energy" then + self.Energy = self.Energy + r.value + elseif r.effect == "healOnAttack" then + self.PlayerHp = self.PlayerHp + r.value + if self.PlayerHp > self.PlayerMaxHp then + self.PlayerHp = self.PlayerMaxHp + end + elseif r.effect == "gold" then + self.Gold = self.Gold + r.value + end + end +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hook' }]), + method('AddRelic', `if self.RunRelics == nil then + self.RunRelics = {} +end +table.insert(self.RunRelics, id) +self:RenderRelics()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]), + method('RenderRelics', `local names = "" +if self.RunRelics ~= nil then + for i = 1, #self.RunRelics do + local r = self.Relics[self.RunRelics[i]] + if r ~= nil then + if names == "" then + names = r.name + else + names = names .. ", " .. r.name + end + end + end +end +if names == "" then + names = "없음" +end +self:SetText("/ui/DefaultGroup/CombatHud/Relics", "유물: " .. names)`), +``` + +- [ ] **Step 6: JSON·문법 검사** + +Run: `node -e "JSON.parse(require('fs').readFileSync('data/relics.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs` +Expected: `JSON OK` + 오류 없음 + +- [ ] **Step 7: 커밋** + +```bash +git add data/relics.json tools/gen-slaydeck.mjs +git commit -m "data(E5): 유물 데이터 + 훅 시스템(ApplyRelics/AddRelic/RenderRelics)" +``` + +--- + +### Task 2: 훅 4지점 통합 + 시작/엘리트 획득 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: StartRun에 유물 주입·시작 유물** — StartRun 코드에서 `self.RunActive = true` 다음에 삽입: + +``` +self.RunRelics = {} +${luaRelicsTable(RELICS.relics)} +self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} } +``` +그리고 StartRun의 `self:ShowMap()` **직전**에 삽입: +``` +self:AddRelic("${RELICS.startingRelic}") +``` + +- [ ] **Step 2: StartCombat에 combatStart 훅** — StartCombat 끝 `self:StartPlayerTurn()`를 아래로 교체: + +``` +self:StartPlayerTurn() +self:ApplyRelics("combatStart") +self:RenderCombat() +``` + +- [ ] **Step 3: StartPlayerTurn에 turnStart 훅** — `self.Energy = self.MaxEnergy` 다음 줄에 삽입: + +``` +self:ApplyRelics("turnStart") +``` + +- [ ] **Step 4: PlayCard Attack 분기에 cardPlayed 훅** — PlayCard의 Attack 분기를 교체: + +``` +if c.kind == "Attack" then + if c.damage ~= nil then + self:DealDamageToEnemy(c.damage) + end + self:ApplyRelics("cardPlayed") +elseif c.kind == "Skill" then +``` +(기존: `if c.kind == "Attack" then\n\tif c.damage ~= nil then\n\t\tself:DealDamageToEnemy(c.damage)\n\tend\nelseif c.kind == "Skill" then` 에서 `end` 다음에 `\n\tself:ApplyRelics("cardPlayed")` 추가) + +- [ ] **Step 5: CheckCombatEnd에 combatReward 훅 + 엘리트 유물** — CheckCombatEnd 승리부를 교체: + +``` +if self.EnemyHp <= 0 then + self.CombatOver = true + self.Gold = self.Gold + ${GOLD_PER_WIN} + self:ApplyRelics("combatReward") + self:RenderRun() + local node = self.MapNodes[self.CurrentNodeId] + if node ~= nil and node.type == "elite" then + self:AddRelic(self.RelicPool[math.random(1, #self.RelicPool)]) + end + if node ~= nil and node.type == "boss" then + self:ShowResult("런 클리어!") + self.RunActive = false + else + self:OfferReward() + end +elseif self.PlayerHp <= 0 then + self.CombatOver = true + self:ShowResult("패배...") + self.RunActive = false +end +``` + +- [ ] **Step 6: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 7: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E5): 훅 4지점 통합·시작/엘리트 유물 획득" +``` + +--- + +### Task 3: 상점 유물 (ShowShop/RenderShop/BuyRelic) + 바인딩 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: ShowShop에 유물 선택 추가** — ShowShop의 `self.ShopBought = { false, false, false }` 다음에: + +``` +self.ShopRelic = self.RelicPool[math.random(1, #self.RelicPool)] +self.ShopRelicBought = false +``` + +- [ ] **Step 2: RenderShop 끝에 유물 슬롯 렌더 + BuyRelic 메서드** — RenderShop 코드의 마지막 카드 for-loop `end` 다음(닫는 백틱 직전)에 추가: + +``` +local rr = self.Relics[self.ShopRelic] +if rr ~= nil then + self:SetText("/ui/DefaultGroup/ShopHud/Relic/Label", rr.name .. " — " .. rr.desc) + self:SetText("/ui/DefaultGroup/ShopHud/Relic/Price", string.format("%d", ${RELIC_PRICE}) .. " 골드") + local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic") + if e ~= nil and e.SpriteGUIRendererComponent ~= nil then + if self.ShopRelicBought == true then + e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + else + e.SpriteGUIRendererComponent.Color = Color(0.7, 0.55, 0.85, 1) + end + end +end +``` +그리고 RenderShop 메서드 다음에 BuyRelic 메서드 추가: + +```js + method('BuyRelic', `if self.ShopRelicBought == true then + return +end +if self.Gold < ${RELIC_PRICE} then + return +end +self.Gold = self.Gold - ${RELIC_PRICE} +self:AddRelic(self.ShopRelic) +self.ShopRelicBought = true +self:RenderShop() +self:RenderRun()`), +``` + +- [ ] **Step 3: BindButtons에 유물 슬롯 바인딩** — BindButtons의 shopLeave 바인딩 다음(restLeave 앞)에 삽입: + +``` +local shopRelic = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic") +if shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then + shopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end) +end +``` + +- [ ] **Step 4: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 5: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E5): 상점 유물 슬롯·BuyRelic·바인딩" +``` + +--- + +### Task 4: UI — 유물 바 + 상점 유물 슬롯 + +**Files:** Modify `tools/gen-slaydeck.mjs` (`upsertUi`) + +- [ ] **Step 1: CombatHud 유물 바 추가** — CombatHud의 Floor/Gold for-loop 다음(`const result = entity({` 앞)에 삽입: + +```js + combat.push(entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/Relics', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1000, y: 40 }, pos: { x: 0, y: 430 } }), + sprite({ color: TRANSPARENT }), + text({ value: '유물: 없음', fontSize: 22, bold: true, color: { r: 0.8, g: 0.7, b: 0.95, a: 1 }, alignment: 4 }), + ], + })); +``` + +- [ ] **Step 2: ShopHud 유물 슬롯 추가** — ShopHud의 Leave 버튼 push 직전에 삽입: + +```js + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 560, y: 76 }, pos: { x: 0, y: -190 } }), + sprite({ color: { r: 0.7, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }), + button(), + ], + })); + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic/Label', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 40 }, pos: { x: 0, y: 12 } }), + sprite({ color: TRANSPARENT }), + text({ value: '유물', fontSize: 22, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }), + ], + })); + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic/Price', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 30 }, pos: { x: 0, y: -22 } }), + sprite({ color: TRANSPARENT }), + text({ value: '60 골드', fontSize: 20, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }), + ], + })); +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E5): 유물 바·상점 유물 슬롯 UI" +``` + +--- + +### Task 5: 재생성 + 검증 + +**Files:** 생성물 + +- [ ] **Step 1: 생성** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 메서드·UI·데이터 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ApplyRelics','AddRelic','RenderRelics','BuyRelic'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const sr=j.ContentProto.Json.Methods.find(m=>m.Name==='StartRun').Code; console.log(/ironHeart/.test(sr)&&/강철 심장/.test(sr)?'RELICS OK':'NO RELICS'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/CombatHud/Relics')&&has('/ui/DefaultGroup/ShopHud/Relic/Label')?'UI OK':'UI MISSING')"` +Expected: `METHODS OK` / `RELICS OK` / `UI OK` + +- [ ] **Step 3: 결정성** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: git status** + +Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `data/relics.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). + +- [ ] **Step 5: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(E5): 유물 시스템·UI 반영" +``` + +- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)** + +reload→Play: 시작 유물(강철심장)→전투 시작 PlayerBlock 6 / energyCore 보유 시 턴 에너지 4 / vampire 보유 시 공격 HP+1 / goldIdol 승리 골드+25 / 엘리트 승리→유물 획득(바 갱신) / 상점 유물 구매(골드-60). MCP는 AddRelic/BuyRelic/PlayCard/PickNode 직접 호출 + 로그. + +--- + +## Self-Review +- **Spec coverage:** 데이터/로드/훅메서드(Task1), 4지점통합·시작·엘리트(Task2), 상점유물(Task3), UI(Task4), 검증(Task5). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. +- **Type consistency:** 메서드 `ApplyRelics/AddRelic/RenderRelics/BuyRelic` 정의·호출·바인딩 일치. 속성 `Relics/RunRelics/RelicPool/ShopRelic/ShopRelicBought` 정의(Task1·1)·사용(Task2·3) 일치. UI 경로 `/CombatHud/Relics`·`/ShopHud/Relic/{Label,Price}`가 codeblock(RenderRelics/RenderShop)·생성(Task4)에서 동일. 유물 필드 `name/desc/hook/effect/value` 데이터·luaRelicsTable·ApplyRelics 일치. 상수 `RELIC_PRICE` Task1 정의·Task3 사용. diff --git a/docs/superpowers/plans/2026-06-09-run-loop-core.md b/docs/superpowers/plans/2026-06-09-run-loop-core.md new file mode 100644 index 0000000..41b83c4 --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-run-loop-core.md @@ -0,0 +1,438 @@ +# 런 루프 코어 (TODO E1+E2) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 단일 전투를 연속 N전투 런으로 확장 — 런 상태(HP/골드/덱) 영속 + 승리 후 카드 1택 보상 + 다음 전투 + N전투 후 "런 클리어". + +**Architecture:** 기존 `SlayDeckController`(gen-slaydeck.mjs 생성)에 런 상태·보상 메서드 추가. StartRun(영속 초기화·버튼 1회 바인딩) vs StartCombat(전투별 초기화, RunDeck에서 드로) 분리. RewardHud UI 생성. + +**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play. + +--- + +## File Structure +- Modify: `tools/gen-slaydeck.mjs` — 유일 변경 대상. + - `writeCodeblocks`: 런 상수, 새 속성, OnBeginPlay/StartRun/StartCombat/BindButtons/CheckCombatEnd/OfferReward/ApplyRewardVisual/PickReward/RenderRun/RenderCombat. + - `upsertUi`: CombatHud에 Floor/Gold, RewardHud 그룹 생성, 필터 확장, guid 'rwd' 분기. + +MSW Lua 단위 테스트 불가 → 검증은 생성기 문법·재생성·결정성·메이커 Play. + +--- + +### Task 1: 런 상수·속성·StartRun + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: 런 상수 추가** — `writeCodeblocks()` 함수 본문 첫 줄에 삽입: + +```js + const RUN_LENGTH = 3; + const GOLD_PER_WIN = 15; +``` + +- [ ] **Step 2: 새 속성 추가** — 속성 배열의 `prop('any', 'EnemyName'),` 다음에: + +```js + prop('any', 'RunDeck'), + prop('number', 'Gold', '0'), + prop('number', 'Floor', '0'), + prop('number', 'RunLength', String(RUN_LENGTH)), + prop('any', 'RewardChoices'), + prop('boolean', 'RunActive', 'false'), +``` + +- [ ] **Step 3: OnBeginPlay → StartRun** — `method('OnBeginPlay', \`self:StartCombat()\`),` 를: + +```js + method('OnBeginPlay', `self:StartRun()`), +``` + +- [ ] **Step 4: StartRun 메서드 추가** — OnBeginPlay 다음에 삽입: + +```js + method('StartRun', `self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.Gold = 0 +self.Floor = 0 +self.RunLength = ${RUN_LENGTH} +self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} } +self.RunActive = true +self:BindButtons() +self:StartCombat()`), +``` + +- [ ] **Step 5: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 6: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E1): 런 상태 속성·StartRun 추가" +``` + +--- + +### Task 2: StartCombat 수정 + BindButtons 수정 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: StartCombat 본문 교체** — `method('StartCombat', \`...\`)`의 코드를 아래로(HP 보존·Floor++·RunDeck에서 드로·BindButtons 호출 제거): + +```js + method('StartCombat', `self.MaxEnergy = 3 +self.Turn = 0 +self.Floor = self.Floor + 1 +self.PlayerBlock = 0 +self.EnemyName = ${luaStr(ACTIVE_ENEMY.name)} +self.EnemyMaxHp = ${ACTIVE_ENEMY.maxHp} +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +${luaIntentsTable(ACTIVE_ENEMY.intents)} +self.EnemyIntentIndex = 1 +self.CombatOver = false +self.DiscardPile = {} +self.Hand = {} +${luaCardsTable(CARDS.cards)} +self.DrawPile = {} +for i = 1, #self.RunDeck do + self.DrawPile[i] = self.RunDeck[i] +end +self:Shuffle(self.DrawPile) +self:RenderCombat() +self:StartPlayerTurn()`), +``` + +- [ ] **Step 2: BindButtons에 보상 버튼 바인딩 추가** — BindButtons 코드 끝(마지막 `end` 다음)에 추가. 현재 마지막 부분: +``` +for i = 1, 5 do + local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i)) + if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then + cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end) + end +end +``` +뒤에 이어붙이도록 BindButtons 코드를 아래 전체로 교체: + +```js + method('BindButtons', `local endTurn = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton") +if endTurn ~= nil and endTurn.ButtonComponent ~= nil then + if self.EndTurnHandler ~= nil then + endTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler) + self.EndTurnHandler = nil + end + self.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end) +end +for i = 1, 5 do + local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i)) + if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then + cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end) + end +end +for i = 1, 3 do + local rc = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Reward" .. tostring(i)) + if rc ~= nil and rc.ButtonComponent ~= nil then + rc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end) + end +end +local skip = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Skip") +if skip ~= nil and skip.ButtonComponent ~= nil then + skip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end) +end`), +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E1): StartCombat 런 분리(HP보존·RunDeck드로)·BindButtons 1회+보상버튼" +``` + +--- + +### Task 3: CheckCombatEnd·OfferReward·PickReward·RenderRun + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: CheckCombatEnd 교체** — 보상/런클리어/패배 분기: + +```js + method('CheckCombatEnd', `if self.EnemyHp <= 0 then + self.CombatOver = true + self.Gold = self.Gold + ${GOLD_PER_WIN} + self:RenderRun() + if self.Floor >= self.RunLength then + self:ShowResult("런 클리어!") + self.RunActive = false + else + self:OfferReward() + end +elseif self.PlayerHp <= 0 then + self.CombatOver = true + self:ShowResult("패배...") + self.RunActive = false +end`), +``` + +- [ ] **Step 2: OfferReward·ApplyRewardVisual·PickReward·RenderRun 추가** — RenderCombat 메서드 다음에 삽입: + +```js + method('OfferReward', `local pool = {} +for id, _ in pairs(self.Cards) do + table.insert(pool, id) +end +self.RewardChoices = {} +for i = 1, 3 do + self.RewardChoices[i] = pool[math.random(1, #pool)] + self:ApplyRewardVisual(i, self.RewardChoices[i]) +end +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud") +if hud ~= nil then + hud.Enable = true +end`), + method('ApplyRewardVisual', `local c = self.Cards[cardId] +if c == nil then + return +end +local base = "/ui/DefaultGroup/RewardHud/Reward" .. tostring(slot) +self:SetText(base .. "/Name", c.name) +self:SetText(base .. "/Cost", tostring(c.cost)) +self:SetText(base .. "/Desc", c.desc) +local e = _EntityService:GetEntityByPath(base) +if e ~= nil and e.SpriteGUIRendererComponent ~= nil then + if c.kind == "Attack" then + e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end +end`, [ + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, + ]), + method('PickReward', `if self.CombatOver ~= true or self.RunActive ~= true then + return +end +if slot ~= 0 and self.RewardChoices ~= nil then + local id = self.RewardChoices[slot] + if id ~= nil then + table.insert(self.RunDeck, id) + end +end +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud") +if hud ~= nil then + hud.Enable = false +end +self:StartCombat()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('RenderRun', `self:SetText("/ui/DefaultGroup/CombatHud/Floor", "층 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength)) +self:SetText("/ui/DefaultGroup/CombatHud/Gold", "골드 " .. string.format("%d", self.Gold))`), +``` + +- [ ] **Step 3: RenderCombat 끝에 RenderRun 호출 추가** — RenderCombat 코드의 마지막 줄(`...PlayerBlock...`) 다음에 `\nself:RenderRun()` 추가. 즉 RenderCombat 마지막을: +``` +self:SetText("/ui/DefaultGroup/CombatHud/PlayerBlock", "방어 " .. string.format("%d", self.PlayerBlock)) +self:RenderRun() +``` +로. (Edit: 기존 마지막 줄 끝에 `\nself:RenderRun()` 삽입) + +- [ ] **Step 4: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 5: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E2): 보상(OfferReward/PickReward)·런 클리어·층/골드 렌더" +``` + +--- + +### Task 4: UI — CombatHud 층/골드 + RewardHud + +**Files:** Modify `tools/gen-slaydeck.mjs` (`upsertUi`, `guid`) + +- [ ] **Step 1: guid 'rwd' 분기 추가** — guid()의 ns 매핑을: + +```js + const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : 0xfe; +``` + +- [ ] **Step 2: 정리 필터 확장** — upsertUi 시작부 필터를: + +```js + ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud')); +``` + +- [ ] **Step 3: CombatHud에 Floor·Gold 텍스트 추가** — `const result = entity({` 선언 직전(즉 result 추가 전)에 삽입: + +```js + for (const [suffix, pos, value, color] of [ + ['Floor', { x: -820, y: 480 }, '층 1/3', GOLD], + ['Gold', { x: 820, y: 480 }, '골드 0', { r: 0.98, g: 0.85, b: 0.4, a: 1 }], + ]) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 240, y: 44 }, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize: 26, bold: true, color, alignment: 4 }), + ], + })); + } +``` + +- [ ] **Step 4: RewardHud 그룹 생성** — `ui.ContentProto.Entities.push(...combat);` 직후, `JSON.parse(JSON.stringify(ui));` 직전에 삽입: + +```js + const reward = []; + const rewardHud = entity({ + id: guid('rwd', 0), + path: '/ui/DefaultGroup/RewardHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 6, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.86 }, type: 1, raycast: true }), + ], + }); + rewardHud.jsonString.enable = false; + reward.push(rewardHud); + reward.push(entity({ + id: guid('rwd', 1), + path: '/ui/DefaultGroup/RewardHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 64 }, pos: { x: 0, y: 300 } }), + sprite({ color: TRANSPARENT }), + text({ value: '보상 카드 선택', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + let rwdN = 2; + const rewardXs = [-300, 0, 300]; + for (let i = 1; i <= 3; i++) { + const cardPath = `/ui/DefaultGroup/RewardHud/Reward${i}`; + reward.push(entity({ + id: guid('rwd', rwdN++), + path: cardPath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: i, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: rewardXs[i - 1], y: 0 } }), + sprite({ color: ATTACK, type: 1, raycast: true }), + button(), + ], + })); + for (const [suffix, cfg] of [ + ['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true }], + ['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true }], + ['Desc', { size: { x: 160, y: 82 }, pos: { x: 0, y: -80 }, value: '', fontSize: 20, bold: false }], + ]) { + reward.push(entity({ + id: guid('rwd', rwdN++), + path: `${cardPath}/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2, + components: [ + transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }), + sprite({ color: TRANSPARENT }), + text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }), + ], + })); + } + } + reward.push(entity({ + id: guid('rwd', rwdN++), + path: '/ui/DefaultGroup/RewardHud/Skip', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 10, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -260 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '건너뛰기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...reward); +``` + +- [ ] **Step 5: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 6: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E2): CombatHud 층/골드 + RewardHud(보상 카드 3+건너뛰기) UI" +``` + +--- + +### Task 5: 재생성 + 검증 + +**Files:** 생성물 2종 + +- [ ] **Step 1: 생성** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 메서드·UI 생성 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['StartRun','OfferReward','PickReward','RenderRun','ApplyRewardVisual'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); console.log(u.ContentProto.Entities.some(e=>e.path==='/ui/DefaultGroup/RewardHud')&&u.ContentProto.Entities.some(e=>e.path==='/ui/DefaultGroup/CombatHud/Gold')?'UI OK':'UI MISSING')"` +Expected: `METHODS OK` / `UI OK` + +- [ ] **Step 3: 결정성** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: git status (의도 파일만)** + +Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). + +- [ ] **Step 5: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(E1+E2): 런 루프·보상 UI 반영" +``` + +- [ ] **Step 6: 메이커 Play 수동 검증 (사용자/MCP)** + +reload→Play: 승리 → RewardHud 카드 3장·골드+15·층 표시 → 1택 시 RunDeck+1·다음 전투(HP 유지) → 3전투째 승리 시 "런 클리어!". 패배 시 "패배...". MCP는 `PlayCard`/`EndPlayerTurn`/`PickReward` 직접 호출 + 상태 로그로 검증. + +--- + +## Self-Review +- **Spec coverage:** 상수·속성·StartRun(Task1), StartCombat분리·BindButtons1회(Task2), 보상·런클리어·렌더(Task3), 층/골드·RewardHud UI(Task4), 검증(Task5). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. +- **Type consistency:** 메서드명 `StartRun/StartCombat/BindButtons/CheckCombatEnd/OfferReward/ApplyRewardVisual/PickReward/RenderRun/RenderCombat` 정의·호출 일치. UI 경로 `/ui/DefaultGroup/RewardHud/Reward{1..3}/{Name,Cost,Desc}`·`/Skip`·`/CombatHud/{Floor,Gold}`가 codeblock(ApplyRewardVisual/RenderRun/BindButtons)과 생성(Task4) 일치. 속성 `RunDeck/Gold/Floor/RunLength/RewardChoices/RunActive` 정의(Task1)·사용(Task2·3) 일치. diff --git a/docs/superpowers/plans/2026-06-09-shop-rest.md b/docs/superpowers/plans/2026-06-09-shop-rest.md new file mode 100644 index 0000000..5f0d081 --- /dev/null +++ b/docs/superpowers/plans/2026-06-09-shop-rest.md @@ -0,0 +1,488 @@ +# 상점/휴식 노드 (TODO E4) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 맵에 상점(골드→카드)·휴식(HP 회복) 노드를 추가하고, 진입 시 전투 대신 상점/휴식 UI로 분기. + +**Architecture:** `data/map.json`에 shop/rest 노드 추가(enemy 없음). SlayDeckController에 상점/휴식 메서드, PickNode 타입 분기, ShopHud/RestHud UI. 모두 `gen-slaydeck.mjs`에서 생성. + +**Tech Stack:** Node.js ESM 생성기, MSW Lua codeblock/UI. 검증은 node --check+재생성+결정성+메이커 Play. + +--- + +## File Structure +- Modify: `data/map.json` — 4행, shop/rest 노드. +- Modify: `tools/gen-slaydeck.mjs` — 검증 완화, enemy 조건부 직렬화, 상수, 속성, PickNode 분기, 상점/휴식 메서드, ShopHud/RestHud UI, MapHud y 중앙정렬. + +검증: MSW Lua 단위테스트 불가 → 생성기 문법·재생성·결정성·메이커 Play. + +--- + +### Task 1: 데이터 + 검증완화 + enemy 조건부 직렬화 + 상수·속성 + +**Files:** Modify `data/map.json`, `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: `data/map.json` 교체** + +```json +{ + "start": ["A", "B"], + "nodes": { + "A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] }, + "B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["C", "D"] }, + "C": { "type": "rest", "row": 2, "col": -1, "next": ["E", "F"] }, + "D": { "type": "shop", "row": 2, "col": 1, "next": ["E", "F"] }, + "E": { "type": "elite", "enemy": "slime_elite", "row": 3, "col": -1, "next": ["BOSS"] }, + "F": { "type": "combat", "enemy": "slime", "row": 3, "col": 1, "next": ["BOSS"] }, + "BOSS": { "type": "boss", "enemy": "slime_boss", "row": 4, "col": 0, "next": [] } + } +} +``` + +- [ ] **Step 2: 검증 완화 (enemy 선택적)** — 생성기의 맵 검증 루프를 교체: + +```js +for (const [id, n] of Object.entries(MAP.nodes)) { + if (n.enemy && !ENEMIES.enemies[n.enemy]) throw new Error(`[gen-slaydeck] 노드 ${id}의 enemy 없음: ${n.enemy}`); + for (const nx of n.next) { + if (!MAP.nodes[nx]) throw new Error(`[gen-slaydeck] 노드 ${id}.next에 없는 노드 id: ${nx}`); + } +} +``` + +- [ ] **Step 3: luaMapNodesTable enemy 조건부** — 함수를 교체: + +```js +function luaMapNodesTable(nodes) { + const lines = Object.entries(nodes).map(([id, n]) => { + const nx = '{ ' + n.next.map(luaStr).join(', ') + ' }'; + const enemyField = n.enemy ? `enemy = ${luaStr(n.enemy)}, ` : ''; + return `\t${id} = { type = ${luaStr(n.type)}, ${enemyField}row = ${n.row}, col = ${n.col}, next = ${nx} },`; + }); + return `self.MapNodes = {\n${lines.join('\n')}\n}`; +} +``` + +- [ ] **Step 4: 상수 추가** — `writeCodeblocks()` 안 `const GOLD_PER_WIN = 15;` 다음에: + +```js + const CARD_PRICE = 30; + const REST_HEAL = 30; +``` + +- [ ] **Step 5: 상점 상태 속성 추가** — `prop('string', 'CurrentEnemyId', '""'),` 다음에: + +```js + prop('any', 'ShopChoices'), + prop('any', 'ShopBought'), +``` + +- [ ] **Step 6: JSON·문법 검사** + +Run: `node -e "JSON.parse(require('fs').readFileSync('data/map.json','utf8')); console.log('JSON OK')" && node --check tools/gen-slaydeck.mjs` +Expected: `JSON OK` + 오류 없음 + +- [ ] **Step 7: 커밋** + +```bash +git add data/map.json tools/gen-slaydeck.mjs +git commit -m "data(E4): 상점/휴식 노드 맵 + enemy 선택적 검증/직렬화 + 상수/속성" +``` + +--- + +### Task 2: PickNode 분기 + 상점/휴식 메서드 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: PickNode 교체 (타입 분기)** + +```js + method('PickNode', `if self.RunActive ~= true then + return +end +if self:IsReachable(id) ~= true then + return +end +self.CurrentNodeId = id +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud") +if hud ~= nil then + hud.Enable = false +end +local node = self.MapNodes[id] +if node.type == "shop" then + self:ShowShop() +elseif node.type == "rest" then + self:ShowRest() +else + self.CurrentEnemyId = node.enemy + self:StartCombat() +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]), +``` + +- [ ] **Step 2: 상점/휴식 메서드 추가** — PickNode 메서드 다음에 삽입: + +```js + method('ShowShop', `local pool = {} +for cid, _ in pairs(self.Cards) do + table.insert(pool, cid) +end +self.ShopChoices = {} +self.ShopBought = { false, false, false } +for i = 1, 3 do + self.ShopChoices[i] = pool[math.random(1, #pool)] +end +self:RenderShop() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud") +if hud ~= nil then + hud.Enable = true +end`), + method('RenderShop', `self:SetText("/ui/DefaultGroup/ShopHud/Gold", "골드 " .. string.format("%d", self.Gold)) +for i = 1, 3 do + local cid = self.ShopChoices[i] + local c = self.Cards[cid] + local base = "/ui/DefaultGroup/ShopHud/Card" .. tostring(i) + if c ~= nil then + self:SetText(base .. "/Name", c.name) + self:SetText(base .. "/Cost", tostring(c.cost)) + self:SetText(base .. "/Desc", c.desc) + self:SetText(base .. "/Price", string.format("%d", ${CARD_PRICE}) .. " 골드") + local e = _EntityService:GetEntityByPath(base) + if e ~= nil and e.SpriteGUIRendererComponent ~= nil then + if self.ShopBought[i] == true then + e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + elseif c.kind == "Attack" then + e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end + end + end +end`), + method('BuyCard', `if self.ShopBought == nil or self.ShopBought[slot] == true then + return +end +if self.Gold < ${CARD_PRICE} then + return +end +self.Gold = self.Gold - ${CARD_PRICE} +table.insert(self.RunDeck, self.ShopChoices[slot]) +self.ShopBought[slot] = true +self:RenderShop() +self:RenderRun()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('ShowRest', `local old = self.PlayerHp +self.PlayerHp = self.PlayerHp + ${REST_HEAL} +if self.PlayerHp > self.PlayerMaxHp then + self.PlayerHp = self.PlayerMaxHp +end +local healed = self.PlayerHp - old +self:SetText("/ui/DefaultGroup/RestHud/Info", "HP " .. string.format("%d", old) .. " → " .. string.format("%d", self.PlayerHp) .. " (+" .. string.format("%d", healed) .. ")") +self:RenderCombat() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud") +if hud ~= nil then + hud.Enable = true +end`), + method('LeaveNode', `local s = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud") +if s ~= nil then + s.Enable = false +end +local r = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud") +if r ~= nil then + r.Enable = false +end +self:ShowMap()`), +``` + +- [ ] **Step 3: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E4): PickNode 타입 분기·상점(구매)/휴식(회복) 메서드" +``` + +--- + +### Task 3: BindButtons 바인딩 + +**Files:** Modify `tools/gen-slaydeck.mjs` + +- [ ] **Step 1: BindButtons 맵 노드 루프 다음에 상점/휴식 바인딩 추가** — BindButtons 코드의 맵 노드 for-loop(`...PickNode(nid)...end\nend`) 다음, 닫는 백틱 직전에 삽입. 맵 노드 루프 끝 부분을 아래로 교체: + +```js +for i = 1, #mapNodeIds do + local nid = mapNodeIds[i] + local mn = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. nid) + if mn ~= nil and mn.ButtonComponent ~= nil then + mn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end) + end +end +for i = 1, 3 do + local sc = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Card" .. tostring(i)) + if sc ~= nil and sc.ButtonComponent ~= nil then + sc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end) + end +end +local shopLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Leave") +if shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then + shopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end) +end +local restLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud/Leave") +if restLeave ~= nil and restLeave.ButtonComponent ~= nil then + restLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end) +end`), +``` + +- [ ] **Step 2: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 3: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E4): 상점 구매/나가기·휴식 나가기 버튼 바인딩" +``` + +--- + +### Task 4: ShopHud·RestHud UI + MapHud 4행 정렬 + +**Files:** Modify `tools/gen-slaydeck.mjs` (`guid`, `upsertUi`) + +- [ ] **Step 1: guid 'shp'·'rst' 분기** — ns 매핑에 추가(map 분기 다음): + +```js + const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : prefix === 'shp' ? 0xce : prefix === 'rst' ? 0xcf : 0xfe; +``` + +- [ ] **Step 2: 필터 확장** — upsertUi 필터에 ShopHud·RestHud 추가: + +```js + ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud') && !e.path.startsWith('/ui/DefaultGroup/ShopHud') && !e.path.startsWith('/ui/DefaultGroup/RestHud')); +``` + +- [ ] **Step 3: MapHud 노드 y 중앙정렬** — upsertUi의 노드 pos 계산을 교체: + +```js + const pos = { x: node.col * 180, y: (node.row - (MAX_ROW + 1) / 2) * 140 }; +``` + +- [ ] **Step 4: ShopHud·RestHud 생성** — `ui.ContentProto.Entities.push(...map);` 다음에 삽입: + +```js + const shop = []; + const shopHud = entity({ + id: guid('shp', 0), + path: '/ui/DefaultGroup/ShopHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 8, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.92 }, type: 1, raycast: true }), + ], + }); + shopHud.jsonString.enable = false; + shop.push(shopHud); + shop.push(entity({ + id: guid('shp', 1), + path: '/ui/DefaultGroup/ShopHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 400 } }), + sprite({ color: TRANSPARENT }), + text({ value: '상점', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + shop.push(entity({ + id: guid('shp', 2), + path: '/ui/DefaultGroup/ShopHud/Gold', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 44 }, pos: { x: 0, y: 330 } }), + sprite({ color: TRANSPARENT }), + text({ value: '골드 0', fontSize: 28, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }), + ], + })); + let shpN = 3; + const shopXs = [-300, 0, 300]; + for (let i = 1; i <= 3; i++) { + const cardPath = `/ui/DefaultGroup/ShopHud/Card${i}`; + shop.push(entity({ + id: guid('shp', shpN++), + path: cardPath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: i, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: shopXs[i - 1], y: 20 } }), + sprite({ color: ATTACK, type: 1, raycast: true }), + button(), + ], + })); + for (const [suffix, cfg] of [ + ['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Desc', { size: { x: 160, y: 60 }, pos: { x: 0, y: -50 }, value: '', fontSize: 20, bold: false, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Price', { size: { x: 160, y: 40 }, pos: { x: 0, y: -105 }, value: '30 골드', fontSize: 22, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 } }], + ]) { + shop.push(entity({ + id: guid('shp', shpN++), + path: `${cardPath}/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : suffix === 'Desc' ? 2 : 3, + components: [ + transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }), + sprite({ color: TRANSPARENT }), + text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }), + ], + })); + } + } + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Leave', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 10, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -300 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...shop); + + const rest = []; + const restHud = entity({ + id: guid('rst', 0), + path: '/ui/DefaultGroup/RestHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.08, b: 0.06, a: 0.92 }, type: 1, raycast: true }), + ], + }); + restHud.jsonString.enable = false; + rest.push(restHud); + rest.push(entity({ + id: guid('rst', 1), + path: '/ui/DefaultGroup/RestHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 140 } }), + sprite({ color: TRANSPARENT }), + text({ value: '휴식', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + rest.push(entity({ + id: guid('rst', 2), + path: '/ui/DefaultGroup/RestHud/Info', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 600, y: 50 }, pos: { x: 0, y: 30 } }), + sprite({ color: TRANSPARENT }), + text({ value: 'HP 회복', fontSize: 30, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }), + ], + })); + rest.push(entity({ + id: guid('rst', 3), + path: '/ui/DefaultGroup/RestHud/Leave', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 2, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -120 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...rest); +``` + +- [ ] **Step 5: 문법 검사** + +Run: `node --check tools/gen-slaydeck.mjs` +Expected: 오류 없음 + +- [ ] **Step 6: 커밋** + +```bash +git add tools/gen-slaydeck.mjs +git commit -m "gen-slaydeck(E4): ShopHud/RestHud UI·MapHud 4행 중앙정렬" +``` + +--- + +### Task 5: 재생성 + 검증 + +**Files:** 생성물 + +- [ ] **Step 1: 생성** + +Run: `node tools/gen-slaydeck.mjs` +Expected: `Slay deck UI and combat codeblocks generated.` + +- [ ] **Step 2: 메서드·UI 확인** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('RootDesk/MyDesk/SlayDeckController.codeblock','utf8')); const n=j.ContentProto.Json.Methods.map(m=>m.Name); console.log(['ShowShop','BuyCard','ShowRest','LeaveNode','RenderShop'].every(x=>n.includes(x))?'METHODS OK':'MISSING'); const u=JSON.parse(require('fs').readFileSync('ui/DefaultGroup.ui','utf8')); const has=p=>u.ContentProto.Entities.some(e=>e.path===p); console.log(has('/ui/DefaultGroup/ShopHud/Card1/Price')&&has('/ui/DefaultGroup/RestHud/Info')&&has('/ui/DefaultGroup/MapHud/Node_D')?'UI OK':'UI MISSING')"` +Expected: `METHODS OK` / `UI OK` + +- [ ] **Step 3: 결정성** + +Run: `node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/a.sha && node tools/gen-slaydeck.mjs >/dev/null && sha1sum ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock > /tmp/b.sha && diff /tmp/a.sha /tmp/b.sha && echo DETERMINISTIC` +Expected: `DETERMINISTIC` + +- [ ] **Step 4: git status** + +Run: `git checkout -- Global/common.gamelogic 2>/dev/null; git status --short` +Expected: `data/map.json`, `tools/gen-slaydeck.mjs`, `ui/DefaultGroup.ui`, `RootDesk/MyDesk/SlayDeckController.codeblock` (+docs). + +- [ ] **Step 5: 생성물 커밋** + +```bash +git add ui/DefaultGroup.ui RootDesk/MyDesk/SlayDeckController.codeblock +git commit -m "재생성(E4): 상점/휴식 노드·UI 반영" +``` + +- [ ] **Step 6: 메이커 Play 수동 검증 (MCP)** + +reload→Play: 맵(4행, 2행에 휴식C·상점D) → PickNode("D")→상점(카드3·골드) → BuyCard(골드≥30이면 -30·RunDeck+1·비활성; 부족하면 무시) → LeaveNode→맵 / PickNode("C")→휴식(HP+30 클램프)→LeaveNode→맵 / 전투·보스·런 클리어 회귀 확인. MCP는 PickNode/BuyCard/LeaveNode 직접 호출 + 로그. + +--- + +## Self-Review +- **Spec coverage:** 맵/검증/직렬화/상수/속성(Task1), PickNode분기·상점·휴식(Task2), 바인딩(Task3), UI·MapHud정렬(Task4), 검증(Task5). 스펙 전 항목 매핑. +- **Placeholder scan:** 모든 단계 실제 코드/명령. +- **Type consistency:** 메서드 `PickNode/ShowShop/RenderShop/BuyCard/ShowRest/LeaveNode` 정의·호출·바인딩 일치. 속성 `ShopChoices/ShopBought` 정의(Task1)·사용(Task2) 일치. UI 경로 `/ui/DefaultGroup/ShopHud/Card{1..3}/{Name,Cost,Desc,Price}`·`/Gold`·`/Leave`, `/RestHud/{Info,Leave}`가 codeblock(RenderShop/ShowRest/BindButtons)·생성(Task4)에서 동일. 상수 `CARD_PRICE/REST_HEAL` Task1 정의·Task2 사용 일치. diff --git a/docs/superpowers/specs/2026-06-08-card-combat-integration-design.md b/docs/superpowers/specs/2026-06-08-card-combat-integration-design.md new file mode 100644 index 0000000..6be3ff9 --- /dev/null +++ b/docs/superpowers/specs/2026-06-08-card-combat-integration-design.md @@ -0,0 +1,74 @@ +# 카드 전투 통합 (TODO 항목 B) — 설계 + +> 작성: 2026-06-08 / 상태: 승인됨 / 근거: TODO.md 항목 B + 코드 직접 분석. +> 선행 작업: 항목 C(미커밋 노이즈 정리) 완료 — 작업 트리 클린. + +## 문제 + +현재 `SlayDeckController.codeblock`의 `PlayCard`는 에너지만 차감하고 `Toast(log)`만 띄운다. +실제 전투 상태(적 HP/방어, 플레이어 HP/Block, 적 의도, 승패)가 없어 STS식 덱빌딩 루프가 +닫히지 않는다. 필드 액션 몬스터(`Monster.codeblock` — HP·피격·리스폰)는 카드 시스템과 분리돼 있다. + +## 범위 + +**포함**: 단일 적 카드 전투 루프(데미지·방어·적 의도·턴 진행·승패), 카드 수치화, DeckHud UI 노출. +**제외(금지)**: 로그라이크 메타(E), 신규 카드 대량 추가, 전체 데이터 외부화(D — 본 작업은 인라인 수치화까지). + +## 단일 소스 원칙 + +모든 변경은 `tools/gen-slaydeck.mjs`에서 생성한다. `SlayDeckController.codeblock` / +`ui/DefaultGroup.ui` / `Global/common.gamelogic`을 직접 손으로 편집하지 않는다. +변경 = 생성기 수정 → `node tools/gen-slaydeck.mjs` 재실행. + +## 수치는 임시 placeholder + +> 플레이어 수치는 향후 **캐릭터 특성별**, 몬스터 수치는 **몬스터별**로 다르게 설정 예정. +> 본 작업의 값(플레이어 80 / 적 45 / 의도 10·6·방8)은 루프 검증용 임시값이며, +> D(데이터 외부화) 단계에서 캐릭터/몬스터별 데이터로 분리한다. + +## 설계 + +### 1) 전투 상태 (codeblock 속성 추가) +- 플레이어: `PlayerHp`, `PlayerMaxHp`(임시 80), `PlayerBlock` +- 적: `EnemyHp`, `EnemyMaxHp`(임시 45), `EnemyBlock`, `EnemyIntentIndex` +- `CombatOver`(승패 후 입력 잠금) +- 적은 codeblock 내부 상태로 보유(필드 `Monster.codeblock`과 분리). + +### 2) 카드 데이터 수치화 (desc 파싱 폐기) +| id | 이름 | cost | kind | 효과 | +|----|------|------|------|------| +| Strike | 타격 | 1 | Attack | damage 6 | +| Defend | 방어 | 1 | Skill | block 5 | +| Bash | 강타 | 2 | Attack | damage 10 | + +`desc`는 표시용으로만 유지. 효과는 `damage`/`block` 숫자 필드로 처리. +시작 덱: Strike×5, Defend×4, Bash×1 (10장). + +### 3) 적 의도 — 결정적 사이클 (사용자 선택: A안) +- 의도 사이클(3스텝 회전): `[공격 10] → [공격 6] → [방어 8]` +- 매 플레이어 턴 시작 시 **다음 의도를 미리 표시**. +- 결정적이라 F(밸런스 시뮬레이터)에서 동일 규칙 재현 가능. + +### 4) 전투 규칙 (STS 관례) +- 데미지는 **방어도 먼저 차감** 후 잔여만 HP에 적용. +- 플레이어 Block은 **플레이어 턴 시작 시 0 리셋**, 적 Block은 **적 턴 시작 시 리셋**. +- `PlayCard(slot)`: `kind=="Attack"` → 적 HP 감소(적 Block 우선 차감); + `kind=="Skill"` → 플레이어 Block 증가. +- `EndPlayerTurn` → 적 턴: 적 Block 리셋 → 현재 의도 실행(공격이면 플레이어에 피해, + 방어면 적 Block↑) → 의도 인덱스 전진 → 패배 체크 → 다음 플레이어 턴(Block/에너지 리셋·드로우) + → 다음 의도 표시. +- 승패: 적 HP≤0 → 승리 / 플레이어 HP≤0 → 패배. 승패 시 `CombatOver=true`로 입력 잠금 + + 결과 텍스트 표시 + **보상 훅 자리(E용 주석)**. + +### 5) UI — DeckHud 엔티티 추가 (생성기 생성) +- 상단 적 패널: 적 이름 · `HP 45/45` · `방어 0` · `의도: 공격 10` +- 좌측 플레이어 패널: `HP 80/80` · `방어 0` +- 승패 결과 텍스트(중앙, 평소 숨김 → 승패 시 표시). + +## 검증 (메이커 Play) +- 타격 카드 → 적 HP 감소(적 Block 있으면 먼저 차감). +- 방어 카드 → 플레이어 Block 증가. +- 턴 종료 → 적이 표시된 의도대로 공격(플레이어 Block이 피해 흡수) 또는 방어. +- 적 HP 0 → 승리 / 플레이어 HP 0 → 패배, 입력 잠금. +- `node tools/gen-slaydeck.mjs` 2회 실행 결과 동일(결정적). +- `git status` — 의도한 생성물만 변경. diff --git a/docs/superpowers/specs/2026-06-08-deck-controller-fixes-design.md b/docs/superpowers/specs/2026-06-08-deck-controller-fixes-design.md new file mode 100644 index 0000000..4e1a71d --- /dev/null +++ b/docs/superpowers/specs/2026-06-08-deck-controller-fixes-design.md @@ -0,0 +1,37 @@ +# 덱 컨트롤러 코드리뷰 수정 설계 + +- 날짜: 2026-06-08 +- 브랜치: feature/deck-controller-fixes (main 기준) +- 대상: `tools/gen-slaydeck.mjs` (단일 소스) → 재생성으로 `ui/DefaultGroup.ui`·`RootDesk/MyDesk/SlayDeckController.codeblock`·`Global/common.gamelogic` 갱신 + +## 배경 + +PR #6의 `SlayDeckController` 코드 리뷰에서 6건을 발견. 모든 산출물(카드 UI·DeckHud·codeblock·common 패치)은 `tools/gen-slaydeck.mjs` 한 곳에서 생성되므로, 이 생성기를 고치고 재실행하면 전부 반영된다. + +## 수정 항목 + +- **① [Important] EndTurn 핸들러 self 바인딩**: `buttonEntity:ConnectEvent(ButtonClickEvent, self.EndPlayerTurn)` → `ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end)`. 메서드 직접 전달 시 self가 event로 잘못 바인딩되는 문제 제거. (타이머는 이미 클로저 사용 — 일관성) +- **② [Important] Card5 이미지 충돌**: 이미 `gen-slaydeck.upsertUi`가 Card1~5를 동일 텍스트 카드로 통일(ImageRUID='', 틴트, Cost/Name/Desc 추가)하므로 재생성으로 해결됨. 추가 코드 변경 없음 — 검증만. +- **③ [기능] 카드 클릭 = 사용**: + - `upsertUi`의 카드 스타일 루프에서 Card1~5에 `ButtonComponent` 추가 + 카드 스프라이트 `RaycastTarget=true`. + - codeblock에 `PlayCard(slot)` 메서드 추가: `Hand[slot]`의 카드 코스트를 `CARDS`에서 조회 → `Energy >= cost`면 `Energy -= cost`, 효과 표시(토스트/로그, 예: "타격 — 피해 6"), `Hand`에서 제거 후 `DiscardPile`에 삽입, `RenderHand(false)`+`RenderPiles()`. 부족하면 사용 불가(토스트/로그). + - `BindButtons`에서 각 카드의 `ButtonClickEvent`를 `function() self:PlayCard(i) end` 클로저로 연결(루프 변수 i는 Lua에서 반복마다 새 지역변수라 안전). 재연결 전 이전 핸들러 해제. +- **④ [Minor] 카드 데이터 단일화**: `CARDS = { Strike={name,cost,desc,kind}, Defend={...}, Bash={...} }` 테이블을 codeblock 상단에 두고, 시작덱 구성·`ApplyCardVisual`·`PlayCard`가 공유(if/elseif 중복 제거). +- **⑤ [Minor] 매직넘버 상수화**: 손패/드로우 수(5), 시작 에너지(3) 등 의미 있는 상수로. +- **⑥ [Nit] pcall 제거**: `ApplyCardVisual`의 `pcall(function() return Color(...) end)` → 직접 `Color(...)` 호출(틴트는 `CARDS[id].kind`별 색). + +## 효과 표시(③) + +적/데미지 시스템이 없으므로 카드 사용 효과는 **토스트 또는 로그**로만 표현(예: `log("타격 — 피해 6")` 또는 UIToast). 실제 데미지 적용은 범위 밖. + +## 재생성·검증 + +1. `node --check tools/gen-slaydeck.mjs` → `node tools/gen-slaydeck.mjs` +2. 검증(데이터): codeblock에 `PlayCard` 존재, `BindButtons`/EndTurn이 클로저, `CARDS` 단일 테이블, `ApplyCardVisual`에 pcall 없음. DefaultGroup.ui의 Card1~5에 `ButtonComponent` + RaycastTarget true, Card5가 균일 텍스트 카드(ImageRUID 빈값·Cost/Name/Desc 존재). +3. Maker Play: 카드 클릭 → 에너지 감소·카드가 버림더미로·재렌더, EndTurn 버튼 동작, 5장 균일. + +## stash 복구 +이전 Maker 세션에서 stash해 둔 로컬 맵 변경(map02/05/06/07/10/11)을 이 브랜치에 복구해 포함. 단 복구분이 몬스터/타일셋 작업을 유지하는지(되돌리지 않는지) 무결성 검증 후 커밋. 손상/무의미하면 사용자에게 알리고 제외. + +## 범위 밖 (YAGNI) +적 턴, 카드 효과의 실제 전투 적용, 신규 카드 종류. diff --git a/docs/superpowers/specs/2026-06-09-balance-simulator-design.md b/docs/superpowers/specs/2026-06-09-balance-simulator-design.md new file mode 100644 index 0000000..39fbff0 --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-balance-simulator-design.md @@ -0,0 +1,68 @@ +# AI 전투 시뮬레이터 (TODO 항목 F) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO.md 항목 F + gen-slaydeck.mjs 전투 규칙 + D 데이터. +> 선행: D(데이터 외부화) 완료. + +## 문제 + +박재오 강점(백엔드·AI 자동화) 활용처로 기획됐으나 코드 없음. 카드/적 밸런싱을 손으로 +검증해야 한다. 데이터 기반 자동 밸런스 검증 도구가 필요하다. + +## 목표 + +`data/cards.json`·`data/enemies.json`를 입력으로, 전투를 몬테카를로로 N회 자동 시뮬레이션해 +승률·평균 턴·OP 카드 탐지 리포트를 출력하는 오프라인 Node CLI(`tools/sim-balance.mjs`). + +## 설계 + +### 구조 +`tools/sim-balance.mjs` 단일 파일, 섹션 분리: +1. **데이터 로드**: `data/cards.json`·`data/enemies.json`(D와 동일 소스). `activeEnemy` 사용. +2. **시드 PRNG**: mulberry32(시드 고정 → 재현 가능, 데이터 바꾸면 결과 변동). +3. **전투 엔진**(Lua 규칙 미러): 아래 규칙을 JS로 재현. +4. **플레이어 정책**(휴리스틱 A). +5. **집계·리포트**. +6. **CLI 파싱·출력**. + +### 전투 규칙 (gen-slaydeck.mjs Lua와 동일) +- 시작: 플레이어 `hp=PLAYER_HP(상수 80)`, `block=0`; 적 `hp=maxHp`, `block=0`, `intentIdx=0`(0-base). + 덱 = `starterDeck` 셔플(PRNG). +- 플레이어 턴 시작: `energy=3`, `block=0`, 5장 드로우(덱 소진 시 버림 더미 셔플해 재활용). +- 플레이어 행동: 정책이 카드 선택 → 사용 시 `energy -= cost`, `Attack`→적에 `damage`(적 block 우선 차감), + `Skill`→플레이어 `block += block`. 사용 카드는 버림. 더 둘 수 없으면 턴 종료. +- 적 턴: 적 `block=0` → 현재 의도 실행(`Attack`→플레이어에 피해(플레이어 block 우선 차감), + `Defend`→적 `block += value`) → `intentIdx=(intentIdx+1)%len`. +- 승패: 적 hp≤0 승리, 플레이어 hp≤0 패배. 턴 상한 `MAX_TURNS=100`(초과 시 무승부로 집계, 경고). + +### 플레이어 정책 (휴리스틱 A) +매 플레이어 행동 루프: +1. **치사 판단**: 손패의 Attack 카드들로 이번 턴 낼 수 있는 최대 데미지(에너지 한도 내) ≥ + `적 hp + 적 block` 이면 → 그 Attack들을 사용(킬). +2. 아니면 **적 의도가 Attack**이면 → 손패 Defend(Skill+block) 카드를 사용(에너지 닿는 한), + 이후 잔여 에너지로 Attack 사용. +3. 아니면(적 Defend 의도) → Attack 우선 사용. +4. 사용 가능한 카드(에너지≥cost)가 없으면 턴 종료. +- 동률 선택은 에너지 효율(뎀/E 또는 블록/E) 높은 카드 우선. + +### 리포트 지표 +- 전체: 승률(%), 평균·중앙값 턴 수, 승리 시 평균 잔여 HP, 패배율, (무승부 시 경고). +- 카드별: 사용 횟수, 누적 데미지/방어, **에너지당 효율**(Attack=총뎀/총E, Skill=총블록/총E). +- **OP 탐지**: 같은 kind 내 효율이 그 kind 중앙값의 ≥1.5배인 카드를 ⚠️로 플래그. 최다/최소 사용 카드 표기. + +### CLI +`node tools/sim-balance.mjs [N] [--seed S]` — 기본 `N=2000`, `seed=1`. 표 형식 출력. + +### 동기화 위험 +JS 전투 규칙은 Lua(`gen-slaydeck.mjs`)와 **중복**이다(공유 불가). 데이터(JSON)는 공유. +파일 상단에 "전투 규칙 변경 시 gen-slaydeck.mjs Lua와 동기화" 주석 명시. + +## 검증 (TDD + CLI) +- 전투 엔진/정책 핵심을 순수 함수로 분리해 단위 테스트(Node 내장 `node:test`): + 데미지 방어차감, 치사 판단, 적 의도 사이클, 승/패 종료. +- `node tools/sim-balance.mjs` → 승률·턴·카드 통계 출력. +- `data/cards.json`에서 강타 damage↑ → 승률·강타 효율 상승(데이터 반영). +- 동일 시드 2회 → 동일 출력(결정성). + +## 범위 밖 (금지) +- 상태이상·드로우·복합효과, 다중 적, 로그라이크 메타. 메이커 런타임 연동. +- 새 카드/적 추가(현 데이터로 검증). 정책 고도화(MCTS 등). diff --git a/docs/superpowers/specs/2026-06-09-data-externalization-design.md b/docs/superpowers/specs/2026-06-09-data-externalization-design.md new file mode 100644 index 0000000..5f248a5 --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-data-externalization-design.md @@ -0,0 +1,89 @@ +# 카드/적 데이터 외부화 (TODO 항목 D) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO.md 항목 D + gen-slaydeck.mjs 분석. +> 선행: B(전투 통합)·A(문서 정합) 완료. F(밸런스 시뮬레이터)의 선행 조건. + +## 문제 + +카드 정의(`self.Cards`)·시작 덱·적 정의(이름/HP/의도)가 `gen-slaydeck.mjs`의 `StartCombat` +Lua 문자열에 하드코딩돼 있다. 카드/적 추가·밸런싱이 생성기 코드 수정을 요구한다. + +## 목표 + +카드·적 데이터를 외부 JSON으로 분리하고, 생성기가 읽어 codeblock·UI에 주입한다. +데이터만 바꿔 재생성하면 게임에 반영(코드 수정 없이). + +## 향후 방향 (참고) + +추후 카드·적 공격은 **메이플스토리 IP**에 맞춰 디벨롭 예정. 본 스키마는 명시적 `desc`와 +키 기반 확장으로 이를 수용한다(새 카드/적은 JSON 항목 추가로 확장). 본 작업은 현 3종+적1 +기준 **최소 스키마**까지만 — 새 효과 필드(상태이상/드로우 등)는 추가하지 않는다(YAGNI). + +## 단일 소스 원칙 + +생성물(`SlayDeckController.codeblock` · `ui/DefaultGroup.ui` · `common.gamelogic`)은 +`gen-slaydeck.mjs`가 생성한다. D 이후 **데이터의 단일 소스는 `data/*.json`**, 생성 로직의 +단일 소스는 `gen-slaydeck.mjs`. 결정적 출력 유지. + +## 설계 + +### 신규 파일 + +**`data/cards.json`** +```json +{ + "cards": { + "Strike": { "name": "타격", "cost": 1, "kind": "Attack", "damage": 6, "desc": "피해 6" }, + "Defend": { "name": "방어", "cost": 1, "kind": "Skill", "block": 5, "desc": "방어도 5" }, + "Bash": { "name": "강타", "cost": 2, "kind": "Attack", "damage": 10, "desc": "피해 10" } + }, + "starterDeck": ["Strike","Strike","Strike","Strike","Strike","Defend","Defend","Defend","Defend","Bash"] +} +``` + +**`data/enemies.json`** +```json +{ + "enemies": { + "slime": { + "name": "슬라임", "maxHp": 45, + "intents": [ + { "kind": "Attack", "value": 10 }, + { "kind": "Attack", "value": 6 }, + { "kind": "Defend", "value": 8 } + ] + } + }, + "activeEnemy": "slime" +} +``` +- `desc`는 명시적(작성자 작성). `kind`은 `"Attack"` 또는 `"Skill"`. 효과는 `damage`/`block`. +- `activeEnemy`로 현재 단일 전투의 적을 데이터에서 지정. 향후 E(맵 노드)에서 노드별 선택으로 확장. + +### 생성기(`gen-slaydeck.mjs`) 변경 +1. 상단에서 `readFileSync`+`JSON.parse`로 `data/cards.json`·`data/enemies.json` 로드. +2. **검증(fail-fast)**: `starterDeck`의 모든 id가 `cards`에 존재해야 함; `activeEnemy`가 + `enemies`에 존재해야 함. 아니면 명확한 에러로 `process.exit(1)`(또는 throw). +3. `writeCodeblocks()`의 `StartCombat`에서: + - `self.Cards = {...}`를 `cards`에서 생성(Lua 테이블 직렬화 헬퍼). + - `self.DrawPile = {...}`를 `starterDeck`에서 생성. + - `self.EnemyName`/`EnemyMaxHp`/`EnemyIntents`/`EnemyIntentIndex`를 `enemies[activeEnemy]`에서 생성. + - codeblock 속성 `EnemyMaxHp` DefaultValue도 데이터 값으로. +4. `upsertUi()`의 DeckHud 카드 미리보기 배열·CombatHud 초기 텍스트(적 이름·`HP n/n`·첫 의도)를 + 동일 데이터에서 파생. +5. Lua 문자열 직렬화 시 한글/따옴표 이스케이프 주의(데이터 값은 따옴표·역슬래시 없는 단순 문자열 가정, + 필요 시 escape 헬퍼). + +### 데이터 흐름 +`data/*.json` → `gen-slaydeck.mjs`(로드·검증·직렬화) → `SlayDeckController.codeblock`(Lua 테이블) ++ `ui/DefaultGroup.ui`(초기 텍스트) → 메이커 런타임. + +## 검증 +- `node tools/gen-slaydeck.mjs` 정상; JSON 유효; 2회 실행 결정적. +- `data/cards.json`에서 카드 1장 수치만 변경 → 재생성 → codeblock의 해당 카드 수치 변경 + (생성기/codeblock 직접 수정 없이). +- 잘못된 데이터(starterDeck에 없는 id, 잘못된 activeEnemy) → 생성기가 명확히 실패. +- 메이커 Play: 기존 B 동작과 동일(데이터 동치이므로 회귀 없음). + +## 범위 밖 (금지) +- 새 효과 필드(상태이상·드로우·복합효과) 추가. 새 카드 종류 대량 추가. F(시뮬레이터)·E(메타). diff --git a/docs/superpowers/specs/2026-06-09-floors-design.md b/docs/superpowers/specs/2026-06-09-floors-design.md new file mode 100644 index 0000000..141e325 --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-floors-design.md @@ -0,0 +1,64 @@ +# 다음 층 / 멀티 act (TODO E6a) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO E 분해(E6a) + 기존 맵/전투 구조. +> 선행: E1~E5 완료. 제외: E6b 저장/불러오기(사용자가 안 함으로 결정 — MSW 저장 API 필요). + +## 문제 + +현재 보스 클리어 = 즉시 런 종료. 로그라이크의 "여러 층(act)을 점점 깊이 진행" 느낌이 없다. +보스 클리어 후 다음 막으로 이어지고, 최종 막 보스에서 진짜 런 클리어가 필요하다. + +## 설계 + +### 파라미터 (생성기 상수) +- `ACT_COUNT = 3` (막 수). +- 적 스케일: `mult = 1 + (Act-1)*0.6` → 1막 ×1, 2막 ×1.6, 3막 ×2.2. + +### 상태 재정의 +- 기존 `Floor`를 **현재 막 카운터**(1..ACT_COUNT)로 사용. `RunLength = ACT_COUNT`. +- 맵 내 행 진행은 맵 UI가 표현 → 별도 숫자 표시 없음. + +### 메서드 변경 +- `StartRun`: `Floor = 1`, `RunLength = ${ACT_COUNT}`. (맵 1회 빌드는 그대로.) +- `StartCombat`: `self.Floor = node.row` 줄 **제거**. 적 로드 시 막 스케일 적용: + ```lua + local mult = 1 + (self.Floor - 1) * 0.6 + self.EnemyMaxHp = math.floor(enemy.maxHp * mult) + self.EnemyHp = self.EnemyMaxHp + self.EnemyIntents = {} + for i = 1, #enemy.intents do + self.EnemyIntents[i] = { kind = enemy.intents[i].kind, value = math.floor(enemy.intents[i].value * mult) } + end + ``` + (공유 enemy.intents 변형 금지 — 새 테이블 생성.) +- `CheckCombatEnd` 보스 승리 분기: + ```lua + if node ~= nil and node.type == "boss" then + if self.Floor < self.RunLength then + self.Floor = self.Floor + 1 + self.CurrentNodeId = "" + self.CurrentEnemyId = "" + self:RenderRun() + self:ShowMap() + else + self:ShowResult("런 클리어!") + self.RunActive = false + end + else + self:OfferReward() + end + ``` + (다음 막은 같은 맵 구조 재사용 — CurrentNodeId 리셋만. 적은 막 스케일로 강해짐.) +- HP·골드·덱·유물은 막 간 유지(기존 영속). combatStart 유물은 전투마다 재적용. + +### UI +- `RenderRun`: 층 텍스트를 `"막 " .. Floor .. "/" .. RunLength`로 (라벨 "층"→"막"). 골드 표시 유지. + +## 검증 (메이커 Play) +- 1막 보스(슬라임 킹 120) 처치 → 2막 맵·Floor 2 → 적 HP 스케일(슬라임 45→72, 보스 120→192). +- 3막 보스 처치 → "런 클리어!". HP/골드/덱/유물 막 간 유지. +- 패배 시 종료. 생성기 결정적·JSON 유효. +- (버튼 런타임 — MCP는 PickNode/PlayCard/CheckCombatEnd 직접 호출 + 상태 로그.) + +## 범위 밖 (금지) +- 저장/불러오기(E6b). 막별 다른 맵 디자인·신규 적/배경·막별 보상 차등. diff --git a/docs/superpowers/specs/2026-06-09-map-nodes-design.md b/docs/superpowers/specs/2026-06-09-map-nodes-design.md new file mode 100644 index 0000000..f97afa9 --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-map-nodes-design.md @@ -0,0 +1,82 @@ +# 분기 맵 노드 진행 (TODO E3) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO E 분해(E3) + SlayDeckController/run-loop-core 분석. +> 선행: E1+E2(런 루프 코어) 완료. 후속: E4(상점/휴식)·E5(유물)·E6(보스 연출/저장). + +## 문제 + +E1+E2는 보상 후 자동으로 다음 전투로 넘어간다(고정 N). 로그라이크는 **플레이어가 맵에서 경로를 +선택**해야 한다. 분기 노드 맵과 노드별 적 차등이 필요하다. + +## 범위 + +플레이어가 **분기 맵(작성된 DAG)** 에서 다음 노드를 선택 → 노드 타입(전투/엘리트/보스)대로 전투 +(적은 데이터로 차등) → 보상 → 맵으로 복귀 → 보스 클리어 시 "런 클리어". **상점/휴식·유물·저장· +절차적 생성·연결선 그리기는 범위 밖**. 맵 스키마는 상점/휴식 타입을 미래 수용. + +## 설계 + +### 데이터 +**`data/map.json`** (분기 DAG): +```json +{ + "start": ["A", "B"], + "nodes": { + "A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] }, + "B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["D", "E"] }, + "C": { "type": "elite", "enemy": "slime_elite", "row": 2, "col": -2, "next": ["BOSS"] }, + "D": { "type": "combat", "enemy": "slime", "row": 2, "col": 0, "next": ["BOSS"] }, + "E": { "type": "combat", "enemy": "slime", "row": 2, "col": 2, "next": ["BOSS"] }, + "BOSS": { "type": "boss", "enemy": "slime_boss", "row": 3, "col": 0, "next": [] } + } +} +``` +- `type` ∈ {combat, elite, boss} (이 슬라이스). `enemy`는 enemies.json id. `row`(1=시작), `col`(레이아웃 x 단위), `next`(도달 노드 ids). + +**`data/enemies.json`** 확장: +```json +"slime_elite": { "name": "정예 슬라임", "maxHp": 70, + "intents": [ {Attack 14}, {Attack 8}, {Defend 10} ] }, +"slime_boss": { "name": "슬라임 킹", "maxHp": 120, + "intents": [ {Attack 18}, {Defend 12}, {Attack 10}, {Attack 22} ] } +``` +(`activeEnemy`는 유지하되 런은 맵 노드의 enemy로 전투. F 시뮬레이터는 여전히 activeEnemy 기준 — 맵 적 시뮬은 후속.) + +### 상태 (SlayDeckController 속성 추가) +- `Enemies`(any) — 전체 적 테이블(id→정의). 생성기가 enemies.json 전체 주입. +- `MapNodes`(any) — 그래프(id→{type, enemy, row, col, next}). +- `MapStart`(any) — 1행 노드 id 배열. +- `CurrentNodeId`(string) — 현재 위치("" = 시작 전). +- `CurrentEnemyId`(string) — 현재 전투 적 id. + +### 메서드 +- `StartRun`(수정): 런 상태 초기화 + `Enemies`/`MapNodes`/`MapStart` 세팅 + `CurrentNodeId=""` + + BindButtons(맵 노드 버튼 포함, 1회) → `self:ShowMap()` (기존 StartCombat 대신). +- `ShowMap`(신규): 선택 가능 노드 결정(CurrentNodeId=="" 면 MapStart, 아니면 MapNodes[CurrentNodeId].next). + 각 노드 버튼 활성/비활성·라벨 갱신, 전투 UI 가리고 MapHud 표시(Enable). +- `IsReachable(id)`(헬퍼) — 현재 선택 가능 목록에 id 포함 여부. +- `PickNode(id)`(신규): `IsReachable(id)` 아니면 무시. `CurrentNodeId=id`, + `CurrentEnemyId=MapNodes[id].enemy`, MapHud 숨김 → `StartCombat()`. +- `StartCombat`(수정): 적을 `self.Enemies[self.CurrentEnemyId]`에서 로드(이름/HP/의도). Floor 증가 로직 제거. +- `CheckCombatEnd`(수정): 승리 시 골드+15 → 현재 노드 `type=="boss"`면 `ShowResult("런 클리어!")`+RunActive=false; + 아니면 `OfferReward`. 패배 → "패배..."+RunActive=false. +- `PickReward`(수정): 카드 처리 후 `StartCombat` 대신 `self:ShowMap()`. + +### UI (MapHud, 신규) +- 평소 숨김. 풀스크린 모달 배경 + 제목 "다음 노드 선택". +- 노드 버튼 6개: 위치 = (col×스페이싱, 화면중앙+row×행간), 라벨(전투/엘리트/보스 + 적 이름). +- 선택 가능 노드만 밝게·클릭, 나머지 어둡게(반투명). 클릭 → `PickNode(id)`. +- 연결선은 생략(도달성=활성/비활성으로 표현; 연결선 그리기는 후속 폴리시). + +### 단일 소스 +모든 변경은 `tools/gen-slaydeck.mjs`에서 생성. map.json/enemies.json은 데이터 단일 소스. + +## 검증 (메이커 Play) +- StartRun → MapHud, 1행 A·B만 선택 가능(나머지 비활성). +- A 선택 → 슬라임 전투 → 승리 → 보상 → 맵 복귀, 이제 C·D 선택 가능(B쪽 E는 불가). +- 엘리트 노드 → 정예 슬라임(HP 70) 전투. 보스 노드 → 슬라임 킹(HP 120). +- 보스 승리 → "런 클리어!". 패배 → "패배...". 도달 불가 노드 클릭 → 무시. +- 생성기 결정적, JSON 유효. (버튼 클릭은 런타임 — MCP는 PickNode/PlayCard/PickReward 직접 호출로 검증.) + +## 범위 밖 (금지) +- 상점/휴식 노드 동작(E4)·유물(E5)·저장(E6). 절차적 맵·무작위 분기·연결선 그리기. 새 카드. diff --git a/docs/superpowers/specs/2026-06-09-relics-design.md b/docs/superpowers/specs/2026-06-09-relics-design.md new file mode 100644 index 0000000..d0b2d9e --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-relics-design.md @@ -0,0 +1,69 @@ +# 유물 (TODO E5) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO E 분해(E5) + 기존 전투/맵/상점 구조. +> 선행: E1~E4 완료. 후속: E6(저장/다음 층). 사용자 요청: 획득 3경로(시작·엘리트·상점) 모두. + +## 문제 + +런 영속·맵·보상·상점은 됐으나 유물(패시브 빌드 요소)이 없다. 훅 기반 패시브 + 다양한 획득 경로가 필요하다. + +## 설계 + +### 데이터 `data/relics.json` +```json +{ + "relics": { + "ironHeart": { "name": "강철 심장", "desc": "전투 시작 시 방어도 +6", "hook": "combatStart", "effect": "block", "value": 6 }, + "energyCore": { "name": "에너지 코어", "desc": "턴 시작 시 에너지 +1", "hook": "turnStart", "effect": "energy", "value": 1 }, + "vampire": { "name": "흡혈 송곳니", "desc": "공격 카드 사용 시 HP +1", "hook": "cardPlayed", "effect": "healOnAttack", "value": 1 }, + "goldIdol": { "name": "황금 우상", "desc": "전투 승리 시 골드 +10", "hook": "combatReward", "effect": "gold", "value": 10 } + }, + "startingRelic": "ironHeart", + "relicPool": ["energyCore", "vampire", "goldIdol"] +} +``` +- `relicPool` = 엘리트/상점에서 무작위로 줄 후보(시작 유물 제외). 중복 허용(스택). + +### 파라미터 (생성기 상수) +- `RELIC_PRICE = 60`. + +### 상태 추가 +- `Relics`(any) — 전체 유물 정의(주입). +- `RunRelics`(any) — 보유 유물 id 목록. +- `ShopRelic`(string) — 상점 제시 유물 id. +- `ShopRelicBought`(boolean). + +### 훅 시스템 +- `ApplyRelics(hook)`: RunRelics 순회, `hook` 일치 유물의 effect 적용: + - `block`→PlayerBlock+=value, `energy`→Energy+=value, `healOnAttack`→PlayerHp+=value(상한 클램프), `gold`→Gold+=value. +- 연결 지점: + - `combatStart` → StartCombat 끝(StartPlayerTurn 호출 뒤 — 방어도 리셋 이후 적용 → RenderCombat). + - `turnStart` → StartPlayerTurn(에너지 회복 직후). + - `cardPlayed` → PlayCard의 Attack 분기(데미지 적용 후). + - `combatReward` → CheckCombatEnd 승리(기본 골드 += 후). + +### 획득 (공통 `AddRelic(id)` → RunRelics 추가·RenderRelics) +- **C 시작**: `StartRun`에서 `RunRelics={}` → `AddRelic(startingRelic)`. +- **A 엘리트**: `CheckCombatEnd` 승리 시 노드 `type=="elite"`면 `relicPool`에서 무작위 `AddRelic`(보스는 런 종료라 제외). +- **B 상점**: `ShowShop`에서 `ShopRelic = relicPool 무작위`, ShopRelicBought=false; `BuyRelic`(ShopRelicBought거나 Gold 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO.md 항목 E(분해) + SlayDeckController 분석. +> E(로그라이크 메타)의 첫 하위 프로젝트. 선행: B·D 완료. 후속: E3(맵)·E4(상점)·E5(유물)·E6(보스/저장). + +## 문제 + +단일 전투(B)는 닫혔으나 승리 후 보상·다음 전투·덱 성장이 없다(보상 훅 자리만 비어 있음). +전투를 한 "런"으로 확장해야 덱빌딩 로그라이크가 된다. + +## 범위 (이 슬라이스) + +전투를 **연속 N전투 런**으로 확장: 런 상태 영속(HP/골드/덱) + 승리 후 카드 1택 보상 + +다음 전투 연결 + 고정 N전투 후 "런 클리어". **맵 노드·상점·유물·보스·저장은 범위 밖**(후속 E3~E6). +아키텍처: 기존 `SlayDeckController` 확장(별도 RunState 분리는 후속). + +## 설계 + +### 런 파라미터 (생성기 상수 — 향후 외부화) +- `RUN_LENGTH = 3` (런당 전투 수), `GOLD_PER_WIN = 15`. + +### 새 상태 (SlayDeckController 속성) +- `RunDeck`(any) — 보유 카드 id 누적 배열(영속). +- `Gold`(number) — 누적 골드. +- `Floor`(number) — 현재 전투 번호(1-base). +- `RunLength`(number) — 런당 전투 수. +- `RewardChoices`(any) — 현재 제시 중인 보상 카드 id 3개. +- `RunActive`(boolean) — 런 진행 중. +- 플레이어 HP는 전투 간 **유지**(StartCombat에서 리셋 안 함). + +### 메서드 +- `OnBeginPlay` → `self:StartRun()`. +- **`StartRun`**(신규): `PlayerMaxHp=80`, `PlayerHp=PlayerMaxHp`, `Gold=0`, `Floor=0`, + `RunLength=RUN_LENGTH`, `RunDeck = starterDeck 복사`, `RunActive=true` → `BindButtons()`(1회) → `StartCombat()`. +- **`StartCombat`**(수정): `Floor += 1`; 적 데이터(activeEnemy) 세팅; 전투별 리셋(Energy/Turn/Block/ + EnemyHp/EnemyBlock/EnemyIntentIndex/DiscardPile/Hand/CombatOver); `DrawPile = RunDeck 복사` → Shuffle; + `Cards` 테이블 세팅. **HP·Gold·RunDeck 보존, BindButtons 호출 제거.** → RenderCombat → StartPlayerTurn. +- **`BindButtons`**(수정): EndTurn·카드5·**보상카드3·건너뛰기** 버튼을 1회 바인딩(StartRun에서 호출). +- **`CheckCombatEnd`**(수정): + - 적 HP≤0(승리): `Gold += GOLD_PER_WIN`; `CombatOver=true`; + `Floor >= RunLength`이면 `ShowResult("런 클리어!")` + `RunActive=false`; + 아니면 `self:OfferReward()`. + - 플레이어 HP≤0(패배): `CombatOver=true`; `ShowResult("패배...")`; `RunActive=false`. +- **`OfferReward`**(신규): `RewardChoices = 카드풀에서 3개 무작위`(math.random); 각 보상 카드 UI 갱신 + (이름/코스트/설명/색); RewardHud 표시(Enable). +- **`PickReward(slot)`**(신규): `slot`(1~3)이면 `RewardChoices[slot]`을 `RunDeck`에 추가; `slot=0`(건너뛰기)이면 추가 안 함; + RewardHud 숨김 → `StartCombat()`(다음 층). +- **`RenderRun`**(신규): `층 Floor/RunLength`·`골드 Gold` 텍스트 갱신. RenderCombat에서 호출. + +### UI (생성기 신규) +- `RewardHud`(평소 숨김): 제목 "보상 카드 선택" + 보상 카드 3장(UISprite+버튼, 이름/코스트/설명 자식) + "건너뛰기" 버튼. +- HUD 표시 추가: `/ui/DefaultGroup/CombatHud/Floor`("층 1/3"), `/Gold`("골드 0"). +- 보상 카드 클릭 → `PickReward(slot)`, 건너뛰기 → `PickReward(0)`. + +### 버그 예방 +- `BindButtons`가 매 전투(StartCombat)마다 카드 버튼에 `ConnectEvent` → 런에서 핸들러 중첩. + **StartRun에서 1회만 바인딩**으로 이동(StartCombat의 BindButtons 호출 제거). + +## 검증 (메이커 Play) +- 전투 승리 → RewardHud에 카드 3장 표시; 골드 +15·층 표시. +- 보상 1택 → RunDeck +1(다음 전투 손패/덱에 등장 가능), RewardHud 숨김, 다음 전투 시작(HP 유지). +- 건너뛰기 → 덱 변화 없이 다음 전투. +- 3전투째 승리 → "런 클리어!"·런 종료. 도중 패배 → "패배..."·런 종료. +- 카드/보상 버튼 클릭은 런타임(MCP는 `PlayCard`/`EndPlayerTurn`/`PickReward` 직접 호출로 검증). +- 생성기 결정적, JSON 유효. + +## 범위 밖 (금지) +- 맵 노드(E3)·상점/휴식(E4)·유물(E5)·보스/층전환/저장(E6). 골드 소비(E4). 보상 풀 확장(메이플 IP 추후). diff --git a/docs/superpowers/specs/2026-06-09-shop-rest-design.md b/docs/superpowers/specs/2026-06-09-shop-rest-design.md new file mode 100644 index 0000000..7815057 --- /dev/null +++ b/docs/superpowers/specs/2026-06-09-shop-rest-design.md @@ -0,0 +1,70 @@ +# 상점/휴식 노드 (TODO E4) — 설계 + +> 작성: 2026-06-09 / 상태: 승인됨 / 근거: TODO E 분해(E4) + E3 맵 노드 구조. +> 선행: E3(분기 맵) 완료. 후속: E5(유물)·E6(저장). 카드 제거는 덱 보기 UI 필요 → 후속 분리. + +## 문제 + +E3로 분기 맵은 됐으나 모든 노드가 전투다. 골드는 적립만 되고 소비처가 없다. 상점(골드→카드)· +휴식(HP 회복) 노드가 필요하다. + +## 범위 + +맵에 상점/휴식 노드 추가, 진입 시 전투 대신 상점/휴식 UI. 상점 = 카드 구매(골드). 휴식 = HP 회복. +**카드 제거(덱 보기 UI 필요)·유물·저장·휴식 업그레이드는 범위 밖.** + +## 설계 + +### 데이터 (`data/map.json` 교체 — 4행) +```json +{ + "start": ["A", "B"], + "nodes": { + "A": { "type": "combat", "enemy": "slime", "row": 1, "col": -1, "next": ["C", "D"] }, + "B": { "type": "combat", "enemy": "slime", "row": 1, "col": 1, "next": ["C", "D"] }, + "C": { "type": "rest", "row": 2, "col": -1, "next": ["E", "F"] }, + "D": { "type": "shop", "row": 2, "col": 1, "next": ["E", "F"] }, + "E": { "type": "elite", "enemy": "slime_elite", "row": 3, "col": -1, "next": ["BOSS"] }, + "F": { "type": "combat", "enemy": "slime", "row": 3, "col": 1, "next": ["BOSS"] }, + "BOSS": { "type": "boss", "enemy": "slime_boss", "row": 4, "col": 0, "next": [] } + } +} +``` +- rest/shop 노드는 `enemy` 없음. 생성기 검증을 "`enemy` 있을 때만 ENEMIES 확인"으로 완화. + Lua MapNodes 직렬화도 enemy 있을 때만 `enemy = "..."` 포함. + +### 파라미터 (생성기 상수) +- `CARD_PRICE = 30`, `REST_HEAL = 30`. + +### 상태 추가 +- `ShopChoices`(any) — 상점 제시 카드 id 3개. +- `ShopBought`(any) — 슬롯별 구매 여부 {bool×3}. + +### 메서드 +- `PickNode`(수정): CurrentNodeId 세팅·맵 숨김 후 타입 분기 — + `shop`→`ShowShop`, `rest`→`ShowRest`, 그 외→`CurrentEnemyId=node.enemy`·`StartCombat`. +- `ShowShop`(신규): 카드 풀에서 3개 무작위→ShopChoices, ShopBought 초기화(false), + 각 슬롯 비주얼·가격·골드 갱신, ShopHud 표시. +- `BuyCard(slot)`(신규): ShopBought[slot]==true 또는 Gold n.row)); + +const RELICS = JSON.parse(readFileSync('data/relics.json', 'utf8')); +if (!RELICS.relics[RELICS.startingRelic]) throw new Error(`[gen-slaydeck] startingRelic 없음: ${RELICS.startingRelic}`); +for (const id of RELICS.relicPool) { + if (!RELICS.relics[id]) throw new Error(`[gen-slaydeck] relicPool에 없는 유물 id: ${id}`); +} +function luaRelicsTable(relics) { + const lines = Object.entries(relics).map(([id, r]) => + `\t${id} = { name = ${luaStr(r.name)}, desc = ${luaStr(r.desc)}, hook = ${luaStr(r.hook)}, effect = ${luaStr(r.effect)}, value = ${r.value} },`); + return `self.Relics = {\n${lines.join('\n')}\n}`; +} + +function luaIntentsArray(intents) { + return '{ ' + intents.map((it) => `{ kind = ${luaStr(it.kind)}, value = ${it.value} }`).join(', ') + ' }'; +} +function luaEnemiesTable(enemies) { + const lines = Object.entries(enemies).map(([id, e]) => + `\t${id} = { name = ${luaStr(e.name)}, maxHp = ${e.maxHp}, intents = ${luaIntentsArray(e.intents)} },`); + return `self.Enemies = {\n${lines.join('\n')}\n}`; +} +function luaMapNodesTable(nodes) { + const lines = Object.entries(nodes).map(([id, n]) => { + const nx = '{ ' + n.next.map(luaStr).join(', ') + ' }'; + const enemyField = n.enemy ? `enemy = ${luaStr(n.enemy)}, ` : ''; + return `\t${id} = { type = ${luaStr(n.type)}, ${enemyField}row = ${n.row}, col = ${n.col}, next = ${nx} },`; + }); + return `self.MapNodes = {\n${lines.join('\n')}\n}`; +} +function luaStartArray(start) { + return 'self.MapStart = { ' + start.map(luaStr).join(', ') + ' }'; +} + +// Lua 직렬화 헬퍼 +function luaStr(s) { + return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; +} +function luaCardsTable(cards) { + const lines = Object.entries(cards).map(([id, c]) => { + const fields = [`name = ${luaStr(c.name)}`, `cost = ${c.cost}`, `desc = ${luaStr(c.desc)}`, `kind = ${luaStr(c.kind)}`]; + if (c.damage != null) fields.push(`damage = ${c.damage}`); + if (c.block != null) fields.push(`block = ${c.block}`); + return `\t${id} = { ${fields.join(', ')} },`; + }); + return `self.Cards = {\n${lines.join('\n')}\n}`; +} +function luaDeckTable(deck) { + return `self.DrawPile = { ${deck.map(luaStr).join(', ')} }`; +} +function luaIntentsTable(intents) { + const lines = intents.map((it) => `\t{ kind = ${luaStr(it.kind)}, value = ${it.value} },`); + return `self.EnemyIntents = {\n${lines.join('\n')}\n}`; +} +function intentText(it) { + if (it.kind === 'Attack') return `의도: 공격 ${it.value}`; + if (it.kind === 'Defend') return `의도: 방어 ${it.value}`; + return ''; +} + const UI_FILE = 'ui/DefaultGroup.ui'; const COMMON_FILE = 'Global/common.gamelogic'; @@ -8,18 +91,26 @@ const DARK = { r: 0.08, g: 0.09, b: 0.11, a: 0.92 }; const GOLD = { r: 0.94, g: 0.74, b: 0.26, a: 1 }; const ATTACK = { r: 0.86, g: 0.42, b: 0.38, a: 1 }; const DEFEND = { r: 0.42, g: 0.55, b: 0.85, a: 1 }; +const SKILL = { r: 0.46, g: 0.68, b: 0.52, a: 1 }; const CARD_W = 180; const CARD_H = 250; +const CARD_SPACING = 200; const CARD_XS = [-400, -200, 0, 200, 400]; + const ALIGN_CENTER = 0; const ALIGN_BOTTOM_CENTER = 6; function guid(prefix, n) { - return `${prefix}${n.toString(16).padStart(4, '0')}-0000-4000-8000-${n.toString(16).padStart(12, '0')}`; + // 유효한 8-4-4-4-12 hex GUID 생성. prefix는 충돌 방지용 네임스페이스 바이트로 매핑. + const ns = prefix === 'hud' ? 0xd0 : prefix === 'dck' ? 0xca : prefix === 'cmb' ? 0xcb : prefix === 'rwd' ? 0xcc : prefix === 'map' ? 0xcd : prefix === 'shp' ? 0xce : prefix === 'rst' ? 0xcf : prefix === 'menu' ? 0xe0 : 0xfe; + const v = (ns * 0x100000 + n) >>> 0; + return `${v.toString(16).padStart(8, '0')}-0000-4000-8000-${v.toString(16).padStart(12, '0')}`; } function transform({ parentW, parentH, anchor, pivot, size, pos, align = 0 }) { + const offMin = { x: pos.x - pivot.x * size.x, y: pos.y - pivot.y * size.y }; + const offMax = { x: pos.x + (1 - pivot.x) * size.x, y: pos.y + (1 - pivot.y) * size.y }; return { '@type': 'MOD.Core.UITransformComponent', ActivePlatform: 255, @@ -27,8 +118,8 @@ function transform({ parentW, parentH, anchor, pivot, size, pos, align = 0 }) { AnchorsMax: anchor, AnchorsMin: anchor, MobileOnly: false, - OffsetMax: { x: pos.x + (1 - pivot.x) * size.x, y: pos.y + (1 - pivot.y) * size.y }, - OffsetMin: { x: pos.x - pivot.x * size.x, y: pos.y - pivot.y * size.y }, + OffsetMax: offMax, + OffsetMin: offMin, Pivot: pivot, RectSize: size, UIMode: 1, @@ -130,7 +221,7 @@ function text({ value, fontSize, bold = false, color = { r: 1, g: 1, b: 1, a: 1 }; } -function entity({ id, path, modelId, entryId, componentNames, components, displayOrder, enable = true }) { +function entity({ id, path, modelId, entryId, componentNames, components, displayOrder }) { const parts = path.split('/'); const name = parts[parts.length - 1]; return { @@ -141,7 +232,7 @@ function entity({ id, path, modelId, entryId, componentNames, components, displa name, path, nameEditable: true, - enable, + enable: true, visible: true, localize: true, displayOrder, @@ -163,25 +254,19 @@ function entity({ id, path, modelId, entryId, componentNames, components, displa function upsertUi() { const ui = JSON.parse(readFileSync(UI_FILE, 'utf8')); - ui.ContentProto.Entities = ui.ContentProto.Entities.filter( - (e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') - && !e.path.startsWith('/ui/DefaultGroup/MainMenu') - && !e.path.startsWith('/ui/DefaultGroup/CardHand/Card5/'), - ); + const E = ui.ContentProto.Entities; + ui.ContentProto.Entities = E.filter((e) => !e.path.startsWith('/ui/DefaultGroup/DeckHud') && !e.path.startsWith('/ui/DefaultGroup/CombatHud') && !e.path.startsWith('/ui/DefaultGroup/RewardHud') && !e.path.startsWith('/ui/DefaultGroup/MapHud') && !e.path.startsWith('/ui/DefaultGroup/ShopHud') && !e.path.startsWith('/ui/DefaultGroup/RestHud') && !e.path.startsWith('/ui/DefaultGroup/MainMenu')); const byPath = new Map(ui.ContentProto.Entities.map((e) => [e.path, e])); - const cardHand = byPath.get('/ui/DefaultGroup/CardHand'); - if (cardHand) cardHand.jsonString.enable = false; - const cards = [ - { name: '타격', cost: '1', desc: '피해 6', tint: ATTACK }, - { name: '타격', cost: '1', desc: '피해 6', tint: ATTACK }, - { name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND }, - { name: '방어', cost: '1', desc: '방어도 5', tint: DEFEND }, - { name: '강타', cost: '2', desc: '피해 10', tint: ATTACK }, - ]; + // 카드 미리보기(초기 정적 표시 — 런타임 RenderHand가 덮어씀): 카드 종류를 순환해 다양성 표시 + const previewIds = Object.keys(CARDS.cards); + const cards = Array.from({ length: 5 }, (_, i) => { + const c = CARDS.cards[previewIds[i % previewIds.length]]; + return { name: c.name, cost: String(c.cost), desc: c.desc, tint: c.kind === 'Attack' ? ATTACK : DEFEND }; + }); - for (let i = 1; i <= 5; i += 1) { + for (let i = 1; i <= 5; i++) { const card = byPath.get(`/ui/DefaultGroup/CardHand/Card${i}`); if (!card) continue; const tr = card.jsonString['@components'].find((c) => c['@type'] === 'MOD.Core.UITransformComponent'); @@ -193,24 +278,33 @@ function upsertUi() { sp.ImageRUID = { DataId: '' }; sp.Type = 1; sp.Color = cards[i - 1].tint; - card.jsonString.enable = false; + sp.RaycastTarget = true; + const comps = card.jsonString['@components']; + if (!comps.some((c) => c['@type'] === 'MOD.Core.ButtonComponent')) { + comps.push(button()); + } + if (!card.componentNames.includes('MOD.Core.ButtonComponent')) { + card.componentNames += ',MOD.Core.ButtonComponent'; + } + card.jsonString.enable = true; + card.jsonString.visible = true; - for (const [suffix, cfg] of [ + const children = [ ['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: cards[i - 1].cost, fontSize: 34, bold: true }], ['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: cards[i - 1].name, fontSize: 26, bold: true }], ['Desc', { size: { x: 160, y: 82 }, pos: { x: 0, y: -80 }, value: cards[i - 1].desc, fontSize: 20, bold: false }], - ]) { + ]; + for (const [suffix, cfg] of children) { const path = `/ui/DefaultGroup/CardHand/Card${i}/${suffix}`; let child = byPath.get(path); if (!child) { child = entity({ - id: guid('d00e', i * 10 + (suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2)), + id: guid('dck', i * 10 + children.findIndex(([s]) => s === suffix)), path, modelId: 'uitext', entryId: 'UIText', componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2, - enable: false, components: [ transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }), sprite({ color: TRANSPARENT }), @@ -220,7 +314,9 @@ function upsertUi() { ui.ContentProto.Entities.push(child); byPath.set(path, child); } else { - child.jsonString.enable = false; + child.id = guid('dck', i * 10 + children.findIndex(([s]) => s === suffix)); + child.jsonString.enable = true; + child.jsonString.visible = true; child.jsonString['@components'][2].Text = cfg.value; child.jsonString['@components'][2].FontSize = cfg.fontSize; child.jsonString['@components'][2].MaxSize = cfg.fontSize; @@ -228,84 +324,16 @@ function upsertUi() { } } - const ents = []; - const add = (e) => ents.push(e); + const hud = []; + const add = (e) => hud.push(e); add(entity({ - id: guid('f00d', 0), - path: '/ui/DefaultGroup/MainMenu', - modelId: 'uisprite', - entryId: 'UISprite', - componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', - displayOrder: 20, - components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), - sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.96 }, type: 1, raycast: true }), - ], - })); - add(entity({ - id: guid('f00d', 1), - path: '/ui/DefaultGroup/MainMenu/Title', - modelId: 'uitext', - entryId: 'UIText', - componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', - displayOrder: 0, - components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 720, y: 100 }, pos: { x: 0, y: 180 }, align: ALIGN_CENTER }), - sprite({ color: TRANSPARENT }), - text({ value: '슬레이 메이플', fontSize: 64, bold: true, color: GOLD, alignment: 0 }), - ], - })); - add(entity({ - id: guid('f00d', 2), - path: '/ui/DefaultGroup/MainMenu/Subtitle', - modelId: 'uitext', - entryId: 'UIText', - componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', - displayOrder: 1, - components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 760, y: 48 }, pos: { x: 0, y: 104 }, align: ALIGN_CENTER }), - sprite({ color: TRANSPARENT }), - text({ value: '카드를 뽑고, 덱을 만들고, 첨탑을 오른다', fontSize: 24, color: { r: 0.82, g: 0.86, b: 0.9, a: 1 }, alignment: 0 }), - ], - })); - add(entity({ - id: guid('f00d', 3), - path: '/ui/DefaultGroup/MainMenu/NewGameButton', - modelId: 'uibutton', - entryId: 'UIButton', - componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', - displayOrder: 2, - components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 260, y: 68 }, pos: { x: 0, y: -20 }, align: ALIGN_CENTER }), - sprite({ color: { r: 0.13, g: 0.15, b: 0.18, a: 1 }, type: 1, raycast: true }), - button(), - text({ value: '새 게임', fontSize: 30, bold: true, color: GOLD, alignment: 0 }), - ], - })); - add(entity({ - id: guid('f00d', 4), - path: '/ui/DefaultGroup/MainMenu/ContinueButton', - modelId: 'uibutton', - entryId: 'UIButton', - componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', - displayOrder: 3, - components: [ - transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 260, y: 58 }, pos: { x: 0, y: -100 }, align: ALIGN_CENTER }), - sprite({ color: { r: 0.1, g: 0.11, b: 0.13, a: 0.78 }, type: 1, raycast: false }), - button({ enabled: false }), - text({ value: '이어하기', fontSize: 24, bold: true, color: { r: 0.55, g: 0.58, b: 0.62, a: 1 }, alignment: 0 }), - ], - })); - - add(entity({ - id: guid('feed', 0), + id: guid('hud', 0), path: '/ui/DefaultGroup/DeckHud', modelId: 'uiempty', entryId: 'UIEmpty', componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', displayOrder: 5, - enable: false, components: [ transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1280, y: 330 }, pos: { x: 0, y: 180 }, align: ALIGN_BOTTOM_CENTER }), sprite({ color: TRANSPARENT }), @@ -313,11 +341,11 @@ function upsertUi() { })); for (const pile of [ - { key: 'DrawPile', x: -590, label: '뽑을 덱', count: '10', color: { r: 0.17, g: 0.2, b: 0.25, a: 1 } }, + { key: 'DrawPile', x: -590, label: '뽑을 덱', count: '10', color: { r: 0.17, g: 0.20, b: 0.25, a: 1 } }, { key: 'DiscardPile', x: 590, label: '버린 덱', count: '0', color: { r: 0.22, g: 0.18, b: 0.16, a: 1 } }, ]) { add(entity({ - id: guid('feed', ents.length), + id: guid('hud', hud.length), path: `/ui/DefaultGroup/DeckHud/${pile.key}`, modelId: 'uisprite', entryId: 'UISprite', @@ -329,7 +357,7 @@ function upsertUi() { ], })); add(entity({ - id: guid('feed', ents.length), + id: guid('hud', hud.length), path: `/ui/DefaultGroup/DeckHud/${pile.key}/Label`, modelId: 'uitext', entryId: 'UIText', @@ -342,7 +370,7 @@ function upsertUi() { ], })); add(entity({ - id: guid('feed', ents.length), + id: guid('hud', hud.length), path: `/ui/DefaultGroup/DeckHud/${pile.key}/Count`, modelId: 'uitext', entryId: 'UIText', @@ -357,7 +385,7 @@ function upsertUi() { } add(entity({ - id: guid('feed', ents.length), + id: guid('hud', hud.length), path: '/ui/DefaultGroup/DeckHud/EndTurnButton', modelId: 'uibutton', entryId: 'UIButton', @@ -370,8 +398,9 @@ function upsertUi() { text({ value: '턴 종료', fontSize: 25, bold: true, color: GOLD, alignment: 0 }), ], })); + add(entity({ - id: guid('feed', ents.length), + id: guid('hud', hud.length), path: '/ui/DefaultGroup/DeckHud/Energy', modelId: 'uitext', entryId: 'UIText', @@ -384,7 +413,538 @@ function upsertUi() { ], })); - ui.ContentProto.Entities.push(...ents); + ui.ContentProto.Entities.push(...hud); + + const PANEL_BG = { r: 0.08, g: 0.09, b: 0.11, a: 0.78 }; + const combat = []; + combat.push(entity({ + id: guid('cmb', 0), + path: '/ui/DefaultGroup/CombatHud', + modelId: 'uiempty', + entryId: 'UIEmpty', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 4, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: TRANSPARENT }), + ], + })); + combat.push(entity({ + id: guid('cmb', 1), + path: '/ui/DefaultGroup/CombatHud/EnemyBg', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 380, y: 170 }, pos: { x: 0, y: 300 }, align: ALIGN_CENTER }), + sprite({ color: PANEL_BG, type: 1 }), + ], + })); + const enemyTexts = [ + ['EnemyName', { x: 0, y: 358 }, { x: 360, y: 44 }, ACTIVE_ENEMY.name, 28, true, GOLD], + ['EnemyHp', { x: 0, y: 316 }, { x: 360, y: 40 }, `HP ${ACTIVE_ENEMY.maxHp}/${ACTIVE_ENEMY.maxHp}`, 24, true, { r: 1, g: 1, b: 1, a: 1 }], + ['EnemyBlock', { x: 0, y: 280 }, { x: 360, y: 36 }, '방어 0', 20, false, { r: 0.6, g: 0.8, b: 1, a: 1 }], + ['EnemyIntent', { x: 0, y: 244 }, { x: 360, y: 38 }, intentText(ACTIVE_ENEMY.intents[0]), 22, true, { r: 1, g: 0.72, b: 0.5, a: 1 }], + ]; + let cmbN = 2; + for (const [suffix, pos, size, value, fontSize, bold, color] of enemyTexts) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: enemyTexts.findIndex(([s]) => s === suffix) + 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize, bold, color }), + ], + })); + } + combat.push(entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/PlayerBg', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 5, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 110 }, pos: { x: -760, y: -260 }, align: ALIGN_CENTER }), + sprite({ color: PANEL_BG, type: 1 }), + ], + })); + const playerTexts = [ + ['PlayerHp', { x: -760, y: -238 }, { x: 280, y: 44 }, 'HP 80/80', 26, true, { r: 1, g: 1, b: 1, a: 1 }], + ['PlayerBlock', { x: -760, y: -284 }, { x: 280, y: 38 }, '방어 0', 22, false, { r: 0.6, g: 0.8, b: 1, a: 1 }], + ]; + for (const [suffix, pos, size, value, fontSize, bold, color] of playerTexts) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 6 + playerTexts.findIndex(([s]) => s === suffix), + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize, bold, color }), + ], + })); + } + for (const [suffix, pos, value, color] of [ + ['Floor', { x: -820, y: 480 }, '층 1/3', GOLD], + ['Gold', { x: 820, y: 480 }, '골드 0', { r: 0.98, g: 0.85, b: 0.4, a: 1 }], + ]) { + combat.push(entity({ + id: guid('cmb', cmbN++), + path: `/ui/DefaultGroup/CombatHud/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 240, y: 44 }, pos }), + sprite({ color: TRANSPARENT }), + text({ value, fontSize: 26, bold: true, color, alignment: 4 }), + ], + })); + } + combat.push(entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/Relics', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1000, y: 40 }, pos: { x: 0, y: 430 } }), + sprite({ color: TRANSPARENT }), + text({ value: '유물: 없음', fontSize: 22, bold: true, color: { r: 0.8, g: 0.7, b: 0.95, a: 1 }, alignment: 4 }), + ], + })); + const result = entity({ + id: guid('cmb', cmbN++), + path: '/ui/DefaultGroup/CombatHud/Result', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 8, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 140 }, pos: { x: 0, y: 120 } }), + sprite({ color: TRANSPARENT }), + text({ value: '', fontSize: 64, bold: true, color: GOLD, alignment: 4 }), + ], + }); + result.jsonString.enable = false; + combat.push(result); + ui.ContentProto.Entities.push(...combat); + + const reward = []; + const rewardHud = entity({ + id: guid('rwd', 0), + path: '/ui/DefaultGroup/RewardHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 6, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.86 }, type: 1, raycast: true }), + ], + }); + rewardHud.jsonString.enable = false; + reward.push(rewardHud); + reward.push(entity({ + id: guid('rwd', 1), + path: '/ui/DefaultGroup/RewardHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 64 }, pos: { x: 0, y: 300 } }), + sprite({ color: TRANSPARENT }), + text({ value: '보상 카드 선택', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + let rwdN = 2; + const rewardXs = [-300, 0, 300]; + for (let i = 1; i <= 3; i++) { + const cardPath = `/ui/DefaultGroup/RewardHud/Reward${i}`; + reward.push(entity({ + id: guid('rwd', rwdN++), + path: cardPath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: i, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: rewardXs[i - 1], y: 0 } }), + sprite({ color: ATTACK, type: 1, raycast: true }), + button(), + ], + })); + for (const [suffix, cfg] of [ + ['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true }], + ['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true }], + ['Desc', { size: { x: 160, y: 82 }, pos: { x: 0, y: -80 }, value: '', fontSize: 20, bold: false }], + ]) { + reward.push(entity({ + id: guid('rwd', rwdN++), + path: `${cardPath}/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : 2, + components: [ + transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }), + sprite({ color: TRANSPARENT }), + text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold }), + ], + })); + } + } + reward.push(entity({ + id: guid('rwd', rwdN++), + path: '/ui/DefaultGroup/RewardHud/Skip', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 10, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -260 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '건너뛰기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...reward); + + const TYPE_KO = { combat: '전투', elite: '엘리트', boss: '보스', shop: '상점', rest: '휴식' }; + const map = []; + const mapHud = entity({ + id: guid('map', 0), + path: '/ui/DefaultGroup/MapHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 7, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.9 }, type: 1, raycast: true }), + ], + }); + mapHud.jsonString.enable = false; + map.push(mapHud); + map.push(entity({ + id: guid('map', 1), + path: '/ui/DefaultGroup/MapHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 510 } }), + sprite({ color: TRANSPARENT }), + text({ value: '다음 노드 선택', fontSize: 40, bold: true, color: GOLD, alignment: 4 }), + ], + })); + let mapN = 2; + for (const [id, node] of Object.entries(MAP.nodes)) { + const nodePath = `/ui/DefaultGroup/MapHud/Node_${id}`; + const pos = { x: node.col * 180, y: (node.row - (MAX_ROW + 1) / 2) * 140 }; + map.push(entity({ + id: guid('map', mapN++), + path: nodePath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: node.row, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 150, y: 80 }, pos }), + sprite({ color: { r: 0.3, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }), + button(), + ], + })); + map.push(entity({ + id: guid('map', mapN++), + path: `${nodePath}/Label`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 150, parentH: 80, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 144, y: 72 }, pos: { x: 0, y: 0 } }), + sprite({ color: TRANSPARENT }), + text({ value: node.enemy ? `${TYPE_KO[node.type]}\n${ENEMIES.enemies[node.enemy].name}` : TYPE_KO[node.type], fontSize: 20, bold: true }), + ], + })); + } + ui.ContentProto.Entities.push(...map); + + const shop = []; + const shopHud = entity({ + id: guid('shp', 0), + path: '/ui/DefaultGroup/ShopHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 8, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.06, b: 0.09, a: 0.92 }, type: 1, raycast: true }), + ], + }); + shopHud.jsonString.enable = false; + shop.push(shopHud); + shop.push(entity({ + id: guid('shp', 1), + path: '/ui/DefaultGroup/ShopHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 400 } }), + sprite({ color: TRANSPARENT }), + text({ value: '상점', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + shop.push(entity({ + id: guid('shp', 2), + path: '/ui/DefaultGroup/ShopHud/Gold', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 300, y: 44 }, pos: { x: 0, y: 330 } }), + sprite({ color: TRANSPARENT }), + text({ value: '골드 0', fontSize: 28, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }), + ], + })); + let shpN = 3; + const shopXs = [-300, 0, 300]; + for (let i = 1; i <= 3; i++) { + const cardPath = `/ui/DefaultGroup/ShopHud/Card${i}`; + shop.push(entity({ + id: guid('shp', shpN++), + path: cardPath, + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: i, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: CARD_W, y: CARD_H }, pos: { x: shopXs[i - 1], y: 20 } }), + sprite({ color: ATTACK, type: 1, raycast: true }), + button(), + ], + })); + for (const [suffix, cfg] of [ + ['Cost', { size: { x: 50, y: 50 }, pos: { x: -60, y: 95 }, value: '1', fontSize: 34, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Name', { size: { x: 160, y: 50 }, pos: { x: 0, y: 50 }, value: '카드', fontSize: 26, bold: true, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Desc', { size: { x: 160, y: 60 }, pos: { x: 0, y: -50 }, value: '', fontSize: 20, bold: false, color: { r: 1, g: 1, b: 1, a: 1 } }], + ['Price', { size: { x: 160, y: 40 }, pos: { x: 0, y: -105 }, value: '30 골드', fontSize: 22, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 } }], + ]) { + shop.push(entity({ + id: guid('shp', shpN++), + path: `${cardPath}/${suffix}`, + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: suffix === 'Cost' ? 0 : suffix === 'Name' ? 1 : suffix === 'Desc' ? 2 : 3, + components: [ + transform({ parentW: CARD_W, parentH: CARD_H, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: cfg.size, pos: cfg.pos }), + sprite({ color: TRANSPARENT }), + text({ value: cfg.value, fontSize: cfg.fontSize, bold: cfg.bold, color: cfg.color }), + ], + })); + } + } + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 560, y: 76 }, pos: { x: 0, y: -190 } }), + sprite({ color: { r: 0.7, g: 0.55, b: 0.85, a: 1 }, type: 1, raycast: true }), + button(), + ], + })); + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic/Label', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 40 }, pos: { x: 0, y: 12 } }), + sprite({ color: TRANSPARENT }), + text({ value: '유물', fontSize: 22, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }), + ], + })); + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Relic/Price', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 560, parentH: 76, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 540, y: 30 }, pos: { x: 0, y: -22 } }), + sprite({ color: TRANSPARENT }), + text({ value: '60 골드', fontSize: 20, bold: true, color: { r: 0.98, g: 0.85, b: 0.4, a: 1 }, alignment: 4 }), + ], + })); + shop.push(entity({ + id: guid('shp', shpN++), + path: '/ui/DefaultGroup/ShopHud/Leave', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 10, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -300 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...shop); + + const rest = []; + const restHud = entity({ + id: guid('rst', 0), + path: '/ui/DefaultGroup/RestHud', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 9, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.05, g: 0.08, b: 0.06, a: 0.92 }, type: 1, raycast: true }), + ], + }); + restHud.jsonString.enable = false; + rest.push(restHud); + rest.push(entity({ + id: guid('rst', 1), + path: '/ui/DefaultGroup/RestHud/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 700, y: 60 }, pos: { x: 0, y: 140 } }), + sprite({ color: TRANSPARENT }), + text({ value: '휴식', fontSize: 44, bold: true, color: GOLD, alignment: 4 }), + ], + })); + rest.push(entity({ + id: guid('rst', 2), + path: '/ui/DefaultGroup/RestHud/Info', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 600, y: 50 }, pos: { x: 0, y: 30 } }), + sprite({ color: TRANSPARENT }), + text({ value: 'HP 회복', fontSize: 30, bold: true, color: { r: 1, g: 1, b: 1, a: 1 }, alignment: 4 }), + ], + })); + rest.push(entity({ + id: guid('rst', 3), + path: '/ui/DefaultGroup/RestHud/Leave', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 2, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 200, y: 60 }, pos: { x: 0, y: -120 } }), + sprite({ color: DARK, type: 1, raycast: true }), + button(), + text({ value: '나가기', fontSize: 26, bold: true, color: GOLD, alignment: 4 }), + ], + })); + ui.ContentProto.Entities.push(...rest); + + const menu = []; + menu.push(entity({ + id: guid('menu', 0), + path: '/ui/DefaultGroup/MainMenu', + modelId: 'uisprite', + entryId: 'UISprite', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent', + displayOrder: 20, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 1920, y: 1080 }, pos: { x: 0, y: 0 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.04, g: 0.05, b: 0.07, a: 0.96 }, type: 1, raycast: true }), + ], + })); + menu.push(entity({ + id: guid('menu', 1), + path: '/ui/DefaultGroup/MainMenu/Title', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 0, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 720, y: 100 }, pos: { x: 0, y: 180 }, align: ALIGN_CENTER }), + sprite({ color: TRANSPARENT }), + text({ value: '슬레이 메이플', fontSize: 64, bold: true, color: GOLD, alignment: 0 }), + ], + })); + menu.push(entity({ + id: guid('menu', 2), + path: '/ui/DefaultGroup/MainMenu/Subtitle', + modelId: 'uitext', + entryId: 'UIText', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent', + displayOrder: 1, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 760, y: 48 }, pos: { x: 0, y: 104 }, align: ALIGN_CENTER }), + sprite({ color: TRANSPARENT }), + text({ value: '카드를 뽑고, 덱을 만들고, 첨탑을 오른다', fontSize: 24, color: { r: 0.82, g: 0.86, b: 0.9, a: 1 }, alignment: 0 }), + ], + })); + menu.push(entity({ + id: guid('menu', 3), + path: '/ui/DefaultGroup/MainMenu/NewGameButton', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 2, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 260, y: 68 }, pos: { x: 0, y: -20 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.13, g: 0.15, b: 0.18, a: 1 }, type: 1, raycast: true }), + button(), + text({ value: '새 게임', fontSize: 30, bold: true, color: GOLD, alignment: 0 }), + ], + })); + menu.push(entity({ + id: guid('menu', 4), + path: '/ui/DefaultGroup/MainMenu/ContinueButton', + modelId: 'uibutton', + entryId: 'UIButton', + componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent', + displayOrder: 3, + components: [ + transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 260, y: 58 }, pos: { x: 0, y: -100 }, align: ALIGN_CENTER }), + sprite({ color: { r: 0.1, g: 0.11, b: 0.13, a: 0.78 }, type: 1, raycast: false }), + button({ enabled: false }), + text({ value: '이어하기', fontSize: 24, bold: true, color: { r: 0.55, g: 0.58, b: 0.62, a: 1 }, alignment: 0 }), + ], + })); + ui.ContentProto.Entities.push(...menu); + JSON.parse(JSON.stringify(ui)); writeFileSync(UI_FILE, JSON.stringify(ui, null, 2), 'utf8'); } @@ -393,13 +953,13 @@ function prop(Type, Name, DefaultValue = 'nil') { return { Type, DefaultValue, SyncDirection: 0, Attributes: [], Name }; } -function method(Name, Code, Arguments = []) { +function method(Name, Code, Arguments = [], ExecSpace = 0, ReturnType = 'void') { return { - Return: { Type: 'void', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null }, + Return: { Type: ReturnType, DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null }, Arguments, Code, Scope: 2, - ExecSpace: 6, + ExecSpace, Attributes: [], Name, }; @@ -439,6 +999,12 @@ function codeblock(id, name, properties, methods) { } function writeCodeblocks() { + const RUN_LENGTH = 3; + const GOLD_PER_WIN = 15; + const CARD_PRICE = 30; + const REST_HEAL = 30; + const RELIC_PRICE = 60; + const ACT_COUNT = 3; const combat = codeblock('SlayDeckController', 'SlayDeckController', [ prop('any', 'DrawPile'), prop('any', 'DiscardPile'), @@ -449,110 +1015,102 @@ function writeCodeblocks() { prop('number', 'TweenEventId', '0'), prop('any', 'EndTurnHandler'), prop('any', 'NewGameHandler'), + prop('any', 'Cards'), + prop('number', 'PlayerHp', '0'), + prop('number', 'PlayerMaxHp', '80'), + prop('number', 'PlayerBlock', '0'), + prop('number', 'EnemyHp', '0'), + prop('number', 'EnemyMaxHp', String(ACTIVE_ENEMY.maxHp)), + prop('number', 'EnemyBlock', '0'), + prop('number', 'EnemyIntentIndex', '1'), + prop('boolean', 'CombatOver', 'false'), + prop('any', 'EnemyIntents'), + prop('any', 'EnemyName'), + prop('any', 'RunDeck'), + prop('number', 'Gold', '0'), + prop('number', 'Floor', '0'), + prop('number', 'RunLength', String(RUN_LENGTH)), + prop('any', 'RewardChoices'), + prop('boolean', 'RunActive', 'false'), + prop('any', 'Enemies'), + prop('any', 'MapNodes'), + prop('any', 'MapStart'), + prop('string', 'CurrentNodeId', '""'), + prop('string', 'CurrentEnemyId', '""'), + prop('any', 'ShopChoices'), + prop('any', 'ShopBought'), + prop('any', 'Relics'), + prop('any', 'RunRelics'), + prop('any', 'RelicPool'), + prop('string', 'ShopRelic', '""'), + prop('boolean', 'ShopRelicBought', 'false'), ], [ method('OnBeginPlay', `self:ShowMainMenu()`), method('ShowMainMenu', `self:SetEntityEnabled("/ui/DefaultGroup/MainMenu", true) -self:SetEntityEnabled("/ui/DefaultGroup/CardHand", false) -self:SetEntityEnabled("/ui/DefaultGroup/DeckHud", false) self:BindMenuButtons()`), method('BindMenuButtons', `local buttonEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/MainMenu/NewGameButton") if buttonEntity == nil or buttonEntity.ButtonComponent == nil then -\treturn + return end if self.NewGameHandler ~= nil then -\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler) -\tself.NewGameHandler = nil + buttonEntity:DisconnectEvent(ButtonClickEvent, self.NewGameHandler) + self.NewGameHandler = nil end -self.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, self.StartNewGame)`), +self.NewGameHandler = buttonEntity:ConnectEvent(ButtonClickEvent, function() self:StartNewGame() end)`), method('StartNewGame', `self:SetEntityEnabled("/ui/DefaultGroup/MainMenu", false) -self:SetEntityEnabled("/ui/DefaultGroup/CardHand", true) -self:SetEntityEnabled("/ui/DefaultGroup/DeckHud", true) -self:ConfigureTurnBasedMonsters() -self:ConfigureTurnBasedPlayer() -self:StartCombat()`), +self:StartRun()`), + method('SetEntityEnabled', `local e = _EntityService:GetEntityByPath(path) +if e ~= nil then + e.Enable = enabled +end`, [ + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, + { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'enabled' }, + ]), + method('StartRun', `self.PlayerMaxHp = 80 +self.PlayerHp = self.PlayerMaxHp +self.Gold = 0 +self.Floor = 1 +self.RunLength = ${ACT_COUNT} +self.RunDeck = { ${CARDS.starterDeck.map(luaStr).join(', ')} } +self.RunActive = true +self.RunRelics = {} +${luaRelicsTable(RELICS.relics)} +self.RelicPool = { ${RELICS.relicPool.map(luaStr).join(', ')} } +${luaEnemiesTable(ENEMIES.enemies)} +${luaMapNodesTable(MAP.nodes)} +${luaStartArray(MAP.start)} +self.CurrentNodeId = "" +self.CurrentEnemyId = "" +self:BindButtons() +self:AddRelic("${RELICS.startingRelic}") +self:ShowMap()`), method('StartCombat', `self.MaxEnergy = 3 self.Turn = 0 +local enemy = self.Enemies[self.CurrentEnemyId] +local mult = 1 + (self.Floor - 1) * 0.6 +self.PlayerBlock = 0 +self.EnemyName = enemy.name +self.EnemyMaxHp = math.floor(enemy.maxHp * mult) +self.EnemyHp = self.EnemyMaxHp +self.EnemyBlock = 0 +self.EnemyIntents = {} +for i = 1, #enemy.intents do + self.EnemyIntents[i] = { kind = enemy.intents[i].kind, value = math.floor(enemy.intents[i].value * mult) } +end +self.EnemyIntentIndex = 1 +self.CombatOver = false self.DiscardPile = {} self.Hand = {} -self.DrawPile = { "Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash" } +${luaCardsTable(CARDS.cards)} +self.DrawPile = {} +for i = 1, #self.RunDeck do + self.DrawPile[i] = self.RunDeck[i] +end self:Shuffle(self.DrawPile) -self:BindButtons() -self:StartPlayerTurn()`), - method('ConfigureTurnBasedMonsters', `for mapIndex = 1, 11 do -\tlocal mapName = "map" .. string.format("%02d", mapIndex) -\tfor i = 1, 6 do -\t\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath("/maps/" .. mapName .. "/Monster" .. tostring(i))) -\tend -\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath("/maps/" .. mapName .. "/StaticMonsterTemplate")) -\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath("/maps/" .. mapName .. "/MoveMonsterTemplate")) -\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath("/maps/" .. mapName .. "/ChaseMonsterTemplate")) -\tself:ConfigureMonsterForTurnCombat(_EntityService:GetEntityByPath("/maps/" .. mapName .. "/monster-43")) -end`), - method('ConfigureMonsterForTurnCombat', `if monster == nil then -\treturn -end -if monster.AIWanderComponent ~= nil then -\tmonster.AIWanderComponent.Enable = false -end -if monster.AIChaseComponent ~= nil then -\tmonster.AIChaseComponent.Enable = false -end -if monster.MovementComponent ~= nil then -\tmonster.MovementComponent.Enable = false -end -if monster.RigidbodyComponent ~= nil then -\tmonster.RigidbodyComponent.MoveVelocity = Vector2.zero -\tmonster.RigidbodyComponent.RealMoveVelocity = Vector2.zero -end -if monster.TransformComponent ~= nil then -\tlocal scale = monster.TransformComponent.Scale -\tmonster.TransformComponent.Scale = Vector3(math.abs(scale.x), math.abs(scale.y), scale.z) -end -if monster.StateAnimationComponent ~= nil and monster.SpriteRendererComponent ~= nil then -\tlocal stand = monster.StateAnimationComponent.ActionSheet["stand"] -\tif stand ~= nil and stand ~= "" then -\t\tmonster.SpriteRendererComponent.SpriteRUID = stand -\tend -end`, [{ Type: 'Entity', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'monster' }]), - method('ConfigureTurnBasedPlayer', `local player = nil -pcall(function() -\tif _UserService ~= nil and _UserService.LocalPlayer ~= nil then -\t\tplayer = _UserService.LocalPlayer -\tend -end) -pcall(function() -\tif player == nil and _UserService ~= nil and _UserService.LocalPlayerEntity ~= nil then -\t\tplayer = _UserService.LocalPlayerEntity -\tend -end) -pcall(function() -\tif player == nil and _UserService ~= nil and _UserService.GetLocalPlayer ~= nil then -\t\tplayer = _UserService:GetLocalPlayer() -\tend -end) -if player ~= nil and player.Entity ~= nil then -\tplayer = player.Entity -end -if player == nil then -\treturn -end -if player.PlayerControllerComponent ~= nil then -\tplayer.PlayerControllerComponent.Enable = false -\tpcall(function() player.PlayerControllerComponent.LookDirectionX = 1 end) -end -if player.MovementComponent ~= nil then -\tplayer.MovementComponent.Enable = false -\tpcall(function() player.MovementComponent.InputSpeed = 0 end) -\tpcall(function() player.MovementComponent.JumpForce = 0 end) -end -if player.RigidbodyComponent ~= nil then -\tplayer.RigidbodyComponent.MoveVelocity = Vector2.zero -\tplayer.RigidbodyComponent.RealMoveVelocity = Vector2.zero -end -if player.TransformComponent ~= nil then -\tlocal scale = player.TransformComponent.Scale -\tplayer.TransformComponent.Scale = Vector3(math.abs(scale.x), math.abs(scale.y), scale.z) -end`), +self:RenderCombat() +self:StartPlayerTurn() +self:ApplyRelics("combatStart") +self:RenderCombat()`), method('Shuffle', `if list == nil then \treturn end @@ -560,25 +1118,77 @@ for i = #list, 2, -1 do \tlocal j = math.random(1, i) \tlist[i], list[j] = list[j], list[i] end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'list' }]), - method('BindButtons', `local buttonEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton") -if buttonEntity == nil or buttonEntity.ButtonComponent == nil then -\treturn + method('BindButtons', `local endTurn = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton") +if endTurn ~= nil and endTurn.ButtonComponent ~= nil then + if self.EndTurnHandler ~= nil then + endTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler) + self.EndTurnHandler = nil + end + self.EndTurnHandler = endTurn:ConnectEvent(ButtonClickEvent, function() self:EndPlayerTurn() end) end -if self.EndTurnHandler ~= nil then -\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler) -\tself.EndTurnHandler = nil +for i = 1, 5 do + local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i)) + if cardEntity ~= nil and cardEntity.ButtonComponent ~= nil then + cardEntity:ConnectEvent(ButtonClickEvent, function() self:PlayCard(i) end) + end end -self.EndTurnHandler = buttonEntity:ConnectEvent(ButtonClickEvent, self.EndPlayerTurn)`), +for i = 1, 3 do + local rc = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Reward" .. tostring(i)) + if rc ~= nil and rc.ButtonComponent ~= nil then + rc:ConnectEvent(ButtonClickEvent, function() self:PickReward(i) end) + end +end +local skip = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud/Skip") +if skip ~= nil and skip.ButtonComponent ~= nil then + skip:ConnectEvent(ButtonClickEvent, function() self:PickReward(0) end) +end +local mapNodeIds = { ${Object.keys(MAP.nodes).map(luaStr).join(', ')} } +for i = 1, #mapNodeIds do + local nid = mapNodeIds[i] + local mn = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. nid) + if mn ~= nil and mn.ButtonComponent ~= nil then + mn:ConnectEvent(ButtonClickEvent, function() self:PickNode(nid) end) + end +end +for i = 1, 3 do + local sc = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Card" .. tostring(i)) + if sc ~= nil and sc.ButtonComponent ~= nil then + sc:ConnectEvent(ButtonClickEvent, function() self:BuyCard(i) end) + end +end +local shopLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Leave") +if shopLeave ~= nil and shopLeave.ButtonComponent ~= nil then + shopLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end) +end +local shopRelic = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic") +if shopRelic ~= nil and shopRelic.ButtonComponent ~= nil then + shopRelic:ConnectEvent(ButtonClickEvent, function() self:BuyRelic() end) +end +local restLeave = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud/Leave") +if restLeave ~= nil and restLeave.ButtonComponent ~= nil then + restLeave:ConnectEvent(ButtonClickEvent, function() self:LeaveNode() end) +end`), method('StartPlayerTurn', `self.Turn = self.Turn + 1 self.Energy = self.MaxEnergy +self:ApplyRelics("turnStart") +self.PlayerBlock = 0 self:DrawCards(5) -self:RenderHand(true)`), - method('EndPlayerTurn', `for i = 1, #self.Hand do +self:RenderHand(true) +self:RenderCombat()`), + method('EndPlayerTurn', `if self.CombatOver == true then + return +end +for i = 1, #self.Hand do \ttable.insert(self.DiscardPile, self.Hand[i]) end self.Hand = {} self:RenderHand(false) self:RenderPiles() +self:EnemyTurn() +self:CheckCombatEnd() +if self.CombatOver == true then + return +end _TimerService:SetTimerOnce(function() self:StartPlayerTurn() end, 0.45)`), method('DrawCards', `for i = 1, amount do \tif #self.DrawPile <= 0 then @@ -602,7 +1212,7 @@ self.DiscardPile = {} self:Shuffle(self.DrawPile)`), method('RenderPiles', `self:SetText("/ui/DefaultGroup/DeckHud/DrawPile/Count", tostring(#self.DrawPile)) self:SetText("/ui/DefaultGroup/DeckHud/DiscardPile/Count", tostring(#self.DiscardPile)) -self:SetText("/ui/DefaultGroup/DeckHud/Energy", "에너지 " .. tostring(self.Energy) .. "/" .. tostring(self.MaxEnergy))`), +self:SetText("/ui/DefaultGroup/DeckHud/Energy", "에너지 " .. string.format("%d", self.Energy) .. "/" .. string.format("%d", self.MaxEnergy))`), method('RenderHand', `local drawStart = Vector2(-590, 8) for i = 1, 5 do \tlocal cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(i)) @@ -620,41 +1230,22 @@ for i = 1, 5 do \tend end self:RenderPiles()`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }]), - method('ApplyCardVisual', `local name = cardId -local cost = 0 -local desc = "" -local kind = "Skill" -if cardId == "Strike" then -\tname = "타격" -\tcost = 1 -\tdesc = "피해 6" -\tkind = "Attack" -elseif cardId == "Defend" then -\tname = "방어" -\tcost = 1 -\tdesc = "방어도 5" -\tkind = "Skill" -elseif cardId == "Bash" then -\tname = "강타" -\tcost = 2 -\tdesc = "피해 10" -\tkind = "Attack" + method('ApplyCardVisual', `local c = self.Cards[cardId] +if c == nil then + c = { name = cardId, cost = 0, desc = "", kind = "Skill" } end -self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Cost", tostring(cost)) -self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Name", name) -self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Desc", desc) +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Cost", tostring(c.cost)) +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Name", c.name) +self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Desc", c.desc) local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot)) if cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then -\tlocal ok = false -\tlocal color = nil -\tif kind == "Attack" then -\t\tok, color = pcall(function() return Color(0.86, 0.42, 0.38, 1) end) -\telseif kind == "Skill" then -\t\tok, color = pcall(function() return Color(0.42, 0.55, 0.85, 1) end) -\tend -\tif ok == true and color ~= nil then -\t\tcardEntity.SpriteGUIRendererComponent.Color = color -\tend + if c.kind == "Attack" then + cardEntity.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + cardEntity.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + cardEntity.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end end`, [ { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, @@ -666,13 +1257,6 @@ end`, [ { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'value' }, ]), - method('SetEntityEnabled', `local entity = _EntityService:GetEntityByPath(path) -if entity ~= nil then -\tentity.Enable = enabled -end`, [ - { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'path' }, - { Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'enabled' }, - ]), method('AnimateCardFrom', `local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot)) if cardEntity == nil or cardEntity.UITransformComponent == nil then \treturn @@ -695,7 +1279,374 @@ end, 1 / 60)`, [ { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' }, ]), + method('PlayCard', `if self.CombatOver == true then + return +end +if self.Hand == nil then + return +end +local cardId = self.Hand[slot] +if cardId == nil then + return +end +local c = self.Cards[cardId] +if c == nil then + return +end +if self.Energy < c.cost then + self:Toast("에너지가 부족합니다") + return +end +self.Energy = self.Energy - c.cost +if c.kind == "Attack" then + if c.damage ~= nil then + self:DealDamageToEnemy(c.damage) + end + self:ApplyRelics("cardPlayed") +elseif c.kind == "Skill" then + if c.block ~= nil then + self.PlayerBlock = self.PlayerBlock + c.block + end +end +table.remove(self.Hand, slot) +table.insert(self.DiscardPile, cardId) +self:RenderHand(false) +self:RenderPiles() +self:RenderCombat() +self:CheckCombatEnd()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('Toast', `log(message)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'message' }]), + method('DealDamageToEnemy', `local dmg = amount +if self.EnemyBlock > 0 then + local absorbed = math.min(self.EnemyBlock, dmg) + self.EnemyBlock = self.EnemyBlock - absorbed + dmg = dmg - absorbed +end +self.EnemyHp = self.EnemyHp - dmg +if self.EnemyHp < 0 then + self.EnemyHp = 0 +end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]), + method('DealDamageToPlayer', `local dmg = amount +if self.PlayerBlock > 0 then + local absorbed = math.min(self.PlayerBlock, dmg) + self.PlayerBlock = self.PlayerBlock - absorbed + dmg = dmg - absorbed +end +self.PlayerHp = self.PlayerHp - dmg +if self.PlayerHp < 0 then + self.PlayerHp = 0 +end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'amount' }]), + method('EnemyTurn', `self.EnemyBlock = 0 +local intent = self.EnemyIntents[self.EnemyIntentIndex] +if intent ~= nil then + if intent.kind == "Attack" then + self:DealDamageToPlayer(intent.value) + elseif intent.kind == "Defend" then + self.EnemyBlock = self.EnemyBlock + intent.value + end +end +self.EnemyIntentIndex = self.EnemyIntentIndex + 1 +if self.EnemyIntentIndex > #self.EnemyIntents then + self.EnemyIntentIndex = 1 +end +self:RenderCombat()`), + method('CheckCombatEnd', `if self.EnemyHp <= 0 then + self.CombatOver = true + self.Gold = self.Gold + ${GOLD_PER_WIN} + self:ApplyRelics("combatReward") + self:RenderRun() + local node = self.MapNodes[self.CurrentNodeId] + if node ~= nil and node.type == "elite" then + self:AddRelic(self.RelicPool[math.random(1, #self.RelicPool)]) + end + if node ~= nil and node.type == "boss" then + if self.Floor < self.RunLength then + self.Floor = self.Floor + 1 + self.CurrentNodeId = "" + self.CurrentEnemyId = "" + self:RenderRun() + self:ShowMap() + else + self:ShowResult("런 클리어!") + self.RunActive = false + end + else + self:OfferReward() + end +elseif self.PlayerHp <= 0 then + self.CombatOver = true + self:ShowResult("패배...") + self.RunActive = false +end`), + method('ShowResult', `self:SetText("/ui/DefaultGroup/CombatHud/Result", text) +local entity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CombatHud/Result") +if entity ~= nil then + entity.Enable = true +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'text' }]), + method('RenderCombat', `self:SetText("/ui/DefaultGroup/CombatHud/EnemyName", self.EnemyName) +self:SetText("/ui/DefaultGroup/CombatHud/EnemyHp", "HP " .. string.format("%d", self.EnemyHp) .. "/" .. string.format("%d", self.EnemyMaxHp)) +self:SetText("/ui/DefaultGroup/CombatHud/EnemyBlock", "방어 " .. string.format("%d", self.EnemyBlock)) +local intent = self.EnemyIntents[self.EnemyIntentIndex] +local intentText = "" +if intent ~= nil then + if intent.kind == "Attack" then + intentText = "의도: 공격 " .. tostring(intent.value) + elseif intent.kind == "Defend" then + intentText = "의도: 방어 " .. tostring(intent.value) + end +end +self:SetText("/ui/DefaultGroup/CombatHud/EnemyIntent", intentText) +self:SetText("/ui/DefaultGroup/CombatHud/PlayerHp", "HP " .. string.format("%d", self.PlayerHp) .. "/" .. string.format("%d", self.PlayerMaxHp)) +self:SetText("/ui/DefaultGroup/CombatHud/PlayerBlock", "방어 " .. string.format("%d", self.PlayerBlock)) +self:RenderRun()`), + method('RenderRun', `self:SetText("/ui/DefaultGroup/CombatHud/Floor", "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength)) +self:SetText("/ui/DefaultGroup/CombatHud/Gold", "골드 " .. string.format("%d", self.Gold))`), + method('OfferReward', `local pool = {} +for id, _ in pairs(self.Cards) do + table.insert(pool, id) +end +self.RewardChoices = {} +for i = 1, 3 do + self.RewardChoices[i] = pool[math.random(1, #pool)] + self:ApplyRewardVisual(i, self.RewardChoices[i]) +end +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud") +if hud ~= nil then + hud.Enable = true +end`), + method('ApplyRewardVisual', `local c = self.Cards[cardId] +if c == nil then + return +end +local base = "/ui/DefaultGroup/RewardHud/Reward" .. tostring(slot) +self:SetText(base .. "/Name", c.name) +self:SetText(base .. "/Cost", tostring(c.cost)) +self:SetText(base .. "/Desc", c.desc) +local e = _EntityService:GetEntityByPath(base) +if e ~= nil and e.SpriteGUIRendererComponent ~= nil then + if c.kind == "Attack" then + e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end +end`, [ + { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, + { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, + ]), + method('PickReward', `if self.CombatOver ~= true or self.RunActive ~= true then + return +end +if slot ~= 0 and self.RewardChoices ~= nil then + local id = self.RewardChoices[slot] + if id ~= nil then + table.insert(self.RunDeck, id) + end +end +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RewardHud") +if hud ~= nil then + hud.Enable = false +end +self:ShowMap()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('ApplyRelics', `if self.RunRelics == nil then + return +end +for i = 1, #self.RunRelics do + local r = self.Relics[self.RunRelics[i]] + if r ~= nil and r.hook == hook then + if r.effect == "block" then + self.PlayerBlock = self.PlayerBlock + r.value + elseif r.effect == "energy" then + self.Energy = self.Energy + r.value + elseif r.effect == "healOnAttack" then + self.PlayerHp = self.PlayerHp + r.value + if self.PlayerHp > self.PlayerMaxHp then + self.PlayerHp = self.PlayerMaxHp + end + elseif r.effect == "gold" then + self.Gold = self.Gold + r.value + end + end +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'hook' }]), + method('AddRelic', `if self.RunRelics == nil then + self.RunRelics = {} +end +table.insert(self.RunRelics, id) +self:RenderRelics()`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]), + method('RenderRelics', `local names = "" +if self.RunRelics ~= nil then + for i = 1, #self.RunRelics do + local r = self.Relics[self.RunRelics[i]] + if r ~= nil then + if names == "" then + names = r.name + else + names = names .. ", " .. r.name + end + end + end +end +if names == "" then + names = "없음" +end +self:SetText("/ui/DefaultGroup/CombatHud/Relics", "유물: " .. names)`), + method('ShowMap', `self:RenderMap() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud") +if hud ~= nil then + hud.Enable = true +end`), + method('IsReachable', `local list +if self.CurrentNodeId == "" then + list = self.MapStart +else + local node = self.MapNodes[self.CurrentNodeId] + if node == nil then + return false + end + list = node.next +end +for i = 1, #list do + if list[i] == id then + return true + end +end +return false`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }], 0, 'boolean'), + method('RenderMap', `for id, node in pairs(self.MapNodes) do + local e = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud/Node_" .. id) + if e ~= nil then + local reachable = self:IsReachable(id) + if e.SpriteGUIRendererComponent ~= nil then + if reachable then + e.SpriteGUIRendererComponent.Color = Color(0.3, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + end + end + if e.ButtonComponent ~= nil then + e.ButtonComponent.Enable = reachable + end + end +end`), + method('PickNode', `if self.RunActive ~= true then + return +end +if self:IsReachable(id) ~= true then + return +end +self.CurrentNodeId = id +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/MapHud") +if hud ~= nil then + hud.Enable = false +end +local node = self.MapNodes[id] +if node.type == "shop" then + self:ShowShop() +elseif node.type == "rest" then + self:ShowRest() +else + self.CurrentEnemyId = node.enemy + self:StartCombat() +end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]), + method('ShowShop', `local pool = {} +for cid, _ in pairs(self.Cards) do + table.insert(pool, cid) +end +self.ShopChoices = {} +self.ShopBought = { false, false, false } +for i = 1, 3 do + self.ShopChoices[i] = pool[math.random(1, #pool)] +end +self.ShopRelic = self.RelicPool[math.random(1, #self.RelicPool)] +self.ShopRelicBought = false +self:RenderShop() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud") +if hud ~= nil then + hud.Enable = true +end`), + method('RenderShop', `self:SetText("/ui/DefaultGroup/ShopHud/Gold", "골드 " .. string.format("%d", self.Gold)) +for i = 1, 3 do + local cid = self.ShopChoices[i] + local c = self.Cards[cid] + local base = "/ui/DefaultGroup/ShopHud/Card" .. tostring(i) + if c ~= nil then + self:SetText(base .. "/Name", c.name) + self:SetText(base .. "/Cost", tostring(c.cost)) + self:SetText(base .. "/Desc", c.desc) + self:SetText(base .. "/Price", string.format("%d", ${CARD_PRICE}) .. " 골드") + local e = _EntityService:GetEntityByPath(base) + if e ~= nil and e.SpriteGUIRendererComponent ~= nil then + if self.ShopBought[i] == true then + e.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + elseif c.kind == "Attack" then + e.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1) + elseif c.kind == "Skill" then + e.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1) + else + e.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1) + end + end + end +end +local rr = self.Relics[self.ShopRelic] +if rr ~= nil then + self:SetText("/ui/DefaultGroup/ShopHud/Relic/Label", rr.name .. " — " .. rr.desc) + self:SetText("/ui/DefaultGroup/ShopHud/Relic/Price", string.format("%d", ${RELIC_PRICE}) .. " 골드") + local re = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud/Relic") + if re ~= nil and re.SpriteGUIRendererComponent ~= nil then + if self.ShopRelicBought == true then + re.SpriteGUIRendererComponent.Color = Color(0.2, 0.22, 0.26, 0.6) + else + re.SpriteGUIRendererComponent.Color = Color(0.7, 0.55, 0.85, 1) + end + end +end`), + method('BuyRelic', `if self.ShopRelicBought == true then + return +end +if self.Gold < ${RELIC_PRICE} then + return +end +self.Gold = self.Gold - ${RELIC_PRICE} +self:AddRelic(self.ShopRelic) +self.ShopRelicBought = true +self:RenderShop() +self:RenderRun()`), + method('BuyCard', `if self.ShopBought == nil or self.ShopBought[slot] == true then + return +end +if self.Gold < ${CARD_PRICE} then + return +end +self.Gold = self.Gold - ${CARD_PRICE} +table.insert(self.RunDeck, self.ShopChoices[slot]) +self.ShopBought[slot] = true +self:RenderShop() +self:RenderRun()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]), + method('ShowRest', `local old = self.PlayerHp +self.PlayerHp = self.PlayerHp + ${REST_HEAL} +if self.PlayerHp > self.PlayerMaxHp then + self.PlayerHp = self.PlayerMaxHp +end +local healed = self.PlayerHp - old +self:SetText("/ui/DefaultGroup/RestHud/Info", "HP " .. string.format("%d", old) .. " → " .. string.format("%d", self.PlayerHp) .. " (+" .. string.format("%d", healed) .. ")") +self:RenderCombat() +local hud = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud") +if hud ~= nil then + hud.Enable = true +end`), + method('LeaveNode', `local s = _EntityService:GetEntityByPath("/ui/DefaultGroup/ShopHud") +if s ~= nil then + s.Enable = false +end +local r = _EntityService:GetEntityByPath("/ui/DefaultGroup/RestHud") +if r ~= nil then + r.Enable = false +end +self:ShowMap()`), ]); + for (const m of combat.ContentProto.Json.Methods) { + m.ExecSpace = 6; + } writeFileSync('RootDesk/MyDesk/SlayDeckController.codeblock', JSON.stringify(combat, null, 2), 'utf8'); } @@ -714,4 +1665,4 @@ upsertUi(); writeCodeblocks(); patchCommon(); -console.log('Slay deck UI and main menu generated.'); +console.log('Slay deck UI and combat codeblocks generated.'); diff --git a/tools/sim-balance.mjs b/tools/sim-balance.mjs new file mode 100644 index 0000000..6dba43f --- /dev/null +++ b/tools/sim-balance.mjs @@ -0,0 +1,199 @@ +// AI 전투 밸런스 시뮬레이터 — 오프라인 몬테카를로. +// ⚠️ 전투 규칙은 tools/gen-slaydeck.mjs 의 Lua(SlayDeckController)와 동기화 유지할 것. +// (데이터는 data/*.json 공유, 규칙 로직은 JS로 중복 재현) +import { readFileSync } from 'node:fs'; + +export const PLAYER_HP = 80; // 데이터 미포함 placeholder (codeblock과 일치) +export const ENERGY = 3; +export const HAND_SIZE = 5; +export const MAX_TURNS = 100; + +export function mulberry32(seed) { + let a = seed >>> 0; + return function () { + a |= 0; a = (a + 0x6D2B79F5) | 0; + let t = Math.imul(a ^ (a >>> 15), 1 | a); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +export function shuffle(arr, rng) { + const a = arr.slice(); + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(rng() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +// 방어 우선 차감 후 hp 적용 → { hp, block } +export function applyDamage(hp, block, amount) { + let dmg = amount; + if (block > 0) { + const absorbed = Math.min(block, dmg); + block -= absorbed; + dmg -= absorbed; + } + hp -= dmg; + if (hp < 0) hp = 0; + return { hp, block }; +} + +export function loadData() { + const cardsData = JSON.parse(readFileSync('data/cards.json', 'utf8')); + const enemiesData = JSON.parse(readFileSync('data/enemies.json', 'utf8')); + const enemy = enemiesData.enemies[enemiesData.activeEnemy]; + if (!enemy) throw new Error(`activeEnemy 없음: ${enemiesData.activeEnemy}`); + return { cards: cardsData.cards, starterDeck: cardsData.starterDeck, enemy }; +} + +// 손패에서 다음에 낼 카드의 인덱스 반환(-1=턴 종료). hand=카드 id 배열. +export function chooseAction(hand, cards, energy, enemyHp, enemyBlock, enemyIntent) { + const entries = hand.map((id, i) => ({ id, i })).filter((x) => cards[x.id].cost <= energy); + const attacks = entries.filter((x) => cards[x.id].kind === 'Attack'); + const skills = entries.filter((x) => cards[x.id].kind === 'Skill'); + const dmgEff = (x) => (cards[x.id].damage || 0) / cards[x.id].cost; + const blkEff = (x) => (cards[x.id].block || 0) / cards[x.id].cost; + const bestBy = (list, fn) => list.slice().sort((a, b) => fn(b) - fn(a))[0]; + + // 1) 치사: 에너지 한도 내 효율순 공격 데미지 합 >= 적 유효 hp? + let e = energy, lethalDmg = 0; + for (const x of attacks.slice().sort((a, b) => dmgEff(b) - dmgEff(a))) { + if (cards[x.id].cost <= e) { e -= cards[x.id].cost; lethalDmg += cards[x.id].damage || 0; } + } + if (attacks.length && lethalDmg >= enemyHp + enemyBlock) return bestBy(attacks, dmgEff).i; + + // 2) 적 공격 의도면 방어 우선 + if (enemyIntent && enemyIntent.kind === 'Attack' && skills.length) return bestBy(skills, blkEff).i; + + // 3) 공격 우선, 없으면 스킬, 없으면 종료 + if (attacks.length) return bestBy(attacks, dmgEff).i; + if (skills.length) return bestBy(skills, blkEff).i; + return -1; +} + +function bump(s, cost, dmg, blk) { + s = s || { plays: 0, energy: 0, damage: 0, block: 0 }; + s.plays++; s.energy += cost; s.damage += dmg; s.block += blk; + return s; +} + +// 단일 전투 시뮬. stats(선택): {cardId: {plays,energy,damage,block}} 누적. +// 반환: { win, turns, playerHpRemaining, draw? } +export function simulateCombat(data, rng, stats) { + const { cards, starterDeck, enemy } = data; + let drawPile = shuffle(starterDeck, rng); + let discard = []; + let hand = []; + let pHp = PLAYER_HP, pBlock = 0; + let eHp = enemy.maxHp, eBlock = 0, intentIdx = 0; + let turns = 0; + + function draw(n) { + for (let k = 0; k < n; k++) { + if (drawPile.length === 0) { drawPile = shuffle(discard, rng); discard = []; } + if (drawPile.length === 0) break; + hand.push(drawPile.pop()); + } + } + + while (turns < MAX_TURNS) { + turns++; + let energy = ENERGY; pBlock = 0; hand = []; draw(HAND_SIZE); + while (true) { + const intent = enemy.intents[intentIdx]; + const idx = chooseAction(hand, cards, energy, eHp, eBlock, intent); + if (idx < 0) break; + const id = hand[idx], c = cards[id]; + energy -= c.cost; + if (c.kind === 'Attack') { + const r = applyDamage(eHp, eBlock, c.damage || 0); eHp = r.hp; eBlock = r.block; + if (stats) stats[id] = bump(stats[id], c.cost, c.damage || 0, 0); + } else { + pBlock += c.block || 0; + if (stats) stats[id] = bump(stats[id], c.cost, 0, c.block || 0); + } + hand.splice(idx, 1); discard.push(id); + if (eHp <= 0) return { win: true, turns, playerHpRemaining: pHp }; + } + discard.push(...hand); hand = []; + eBlock = 0; + const intent = enemy.intents[intentIdx]; + if (intent.kind === 'Attack') { const r = applyDamage(pHp, pBlock, intent.value); pHp = r.hp; pBlock = r.block; } + else if (intent.kind === 'Defend') { eBlock += intent.value; } + intentIdx = (intentIdx + 1) % enemy.intents.length; + if (pHp <= 0) return { win: false, turns, playerHpRemaining: 0 }; + } + return { win: false, turns, playerHpRemaining: pHp, draw: true }; +} + +function mean(a) { return a.length ? a.reduce((s, x) => s + x, 0) / a.length : 0; } +function median(a) { + if (!a.length) return 0; + const s = a.slice().sort((x, y) => x - y), m = Math.floor(s.length / 2); + return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; +} + +export function runBatch(N, seed) { + const data = loadData(); + const rng = mulberry32(seed); + const cardStats = {}; + let wins = 0, draws = 0; + const turnsArr = [], hpArr = []; + for (let i = 0; i < N; i++) { + const r = simulateCombat(data, rng, cardStats); + if (r.draw) draws++; + if (r.win) { wins++; hpArr.push(r.playerHpRemaining); } + turnsArr.push(r.turns); + } + return { + N, wins, draws, losses: N - wins - draws, + winRate: wins / N, + avgTurns: mean(turnsArr), medianTurns: median(turnsArr), + avgHpOnWin: mean(hpArr), + cardStats, cards: data.cards, enemy: data.enemy, seed, + }; +} + +export function formatReport(r) { + const L = []; + L.push(`=== 밸런스 시뮬레이션 (적: ${r.enemy.name} HP ${r.enemy.maxHp}) ===`); + L.push(`시뮬 ${r.N}회 (seed=${r.seed})`); + L.push(`승률: ${(r.winRate * 100).toFixed(1)}% (승 ${r.wins} / 패 ${r.losses}${r.draws ? ` / 무 ${r.draws}` : ''})`); + L.push(`평균 턴: ${r.avgTurns.toFixed(2)} 중앙값 턴: ${r.medianTurns}`); + L.push(`승리 시 평균 잔여 HP: ${r.avgHpOnWin.toFixed(1)} / ${PLAYER_HP}`); + if (r.draws) L.push(`⚠️ 무승부 ${r.draws}건 (턴 상한 ${MAX_TURNS} 초과)`); + L.push(''); + L.push('카드별:'); + const rows = Object.entries(r.cardStats).map(([id, s]) => { + const kind = r.cards[id].kind; + const eff = kind === 'Attack' ? s.damage / s.energy : s.block / s.energy; + return { id, name: r.cards[id].name, kind, plays: s.plays, eff }; + }); + for (const kind of ['Attack', 'Skill']) { + const kr = rows.filter((x) => x.kind === kind); + if (!kr.length) continue; + const med = median(kr.map((x) => x.eff)); + const unit = kind === 'Attack' ? '뎀/E' : '블록/E'; + for (const x of kr) { + const op = med > 0 && x.eff >= med * 1.5 ? ' ⚠️ OP 의심' : ''; + L.push(` ${x.name}(${x.id}): 사용 ${x.plays}, 효율 ${x.eff.toFixed(2)} ${unit}${op}`); + } + } + const sorted = rows.slice().sort((a, b) => b.plays - a.plays); + if (sorted.length) L.push(`최다 사용: ${sorted[0].name} / 최소 사용: ${sorted[sorted.length - 1].name}`); + return L.join('\n'); +} + +function main() { + const args = process.argv.slice(2); + let N = 2000, seed = 1; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--seed') seed = parseInt(args[++i], 10); + else if (/^\d+$/.test(args[i])) N = parseInt(args[i], 10); + } + console.log(formatReport(runBatch(N, seed))); +} + +if (process.argv[1] && process.argv[1].endsWith('sim-balance.mjs')) main(); diff --git a/tools/sim-balance.test.mjs b/tools/sim-balance.test.mjs new file mode 100644 index 0000000..303e63f --- /dev/null +++ b/tools/sim-balance.test.mjs @@ -0,0 +1,80 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { + mulberry32, applyDamage, chooseAction, simulateCombat, runBatch, +} from './sim-balance.mjs'; + +test('applyDamage: 방어 우선 차감 후 hp', () => { + assert.deepEqual(applyDamage(80, 0, 10), { hp: 70, block: 0 }); + assert.deepEqual(applyDamage(80, 5, 10), { hp: 75, block: 0 }); + assert.deepEqual(applyDamage(80, 12, 10), { hp: 80, block: 2 }); + assert.deepEqual(applyDamage(3, 0, 10), { hp: 0, block: 0 }); +}); + +test('mulberry32: 동일 시드 동일 수열', () => { + const a = mulberry32(1), b = mulberry32(1); + assert.equal(a(), b()); + assert.equal(a(), b()); +}); + +const CARDS = { + Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 }, + Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 }, + Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 }, +}; + +test('chooseAction: 치사 가능하면 공격 선택', () => { + const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 5, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, 0); +}); + +test('chooseAction: 치사 불가 + 적 공격 의도면 방어 선택', () => { + const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 40, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, 1); +}); + +test('chooseAction: 적 방어 의도면 공격 우선', () => { + const idx = chooseAction(['Defend', 'Strike'], CARDS, 3, 40, 0, { kind: 'Defend', value: 8 }); + assert.equal(idx, 1); +}); + +test('chooseAction: 사용 가능 카드 없으면 -1', () => { + const idx = chooseAction(['Bash'], CARDS, 1, 40, 0, { kind: 'Attack', value: 10 }); + assert.equal(idx, -1); +}); + +const DATA = { + cards: CARDS, + starterDeck: ['Strike', 'Strike', 'Strike', 'Strike', 'Strike', 'Defend', 'Defend', 'Defend', 'Defend', 'Bash'], + enemy: { + name: '슬라임', maxHp: 45, intents: [ + { kind: 'Attack', value: 10 }, { kind: 'Attack', value: 6 }, { kind: 'Defend', value: 8 }, + ], + }, +}; + +test('simulateCombat: 결정적 결과(동일 시드)', () => { + const r1 = simulateCombat(DATA, mulberry32(1)); + const r2 = simulateCombat(DATA, mulberry32(1)); + assert.deepEqual(r1, r2); + assert.equal(typeof r1.win, 'boolean'); + assert.ok(r1.turns >= 1); +}); + +test('simulateCombat: 약한 적이면 대체로 승리', () => { + let wins = 0; + for (let i = 0; i < 50; i++) if (simulateCombat(DATA, mulberry32(i + 1)).win) wins++; + assert.ok(wins >= 40, `예상 승리 다수, 실제 ${wins}/50`); +}); + +test('runBatch: 집계 필드·승률 범위', () => { + const r = runBatch(100, 1); + assert.equal(r.N, 100); + assert.ok(r.winRate >= 0 && r.winRate <= 1); + assert.ok(r.avgTurns > 0); + assert.ok(r.cardStats.Strike.plays > 0); +}); + +test('runBatch: 동일 시드 동일 결과', () => { + assert.deepEqual(runBatch(100, 7), runBatch(100, 7)); +}); diff --git a/ui/DefaultGroup.ui b/ui/DefaultGroup.ui index e126f3c..ced5874 100644 --- a/ui/DefaultGroup.ui +++ b/ui/DefaultGroup.ui @@ -1073,7 +1073,7 @@ "name": "CardHand", "path": "/ui/DefaultGroup/CardHand", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 4, @@ -1209,12 +1209,12 @@ { "id": "cad00001-0000-4000-8000-000000000001", "path": "/ui/DefaultGroup/CardHand/Card1", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { "name": "Card1", "path": "/ui/DefaultGroup/CardHand/Card1", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 0, @@ -1342,20 +1342,67 @@ "RaycastTarget": true, "Type": 1, "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true } ], "@version": 1 } }, { - "id": "cad00002-0000-4000-8000-000000000002", + "id": "0ca0000a-0000-4000-8000-00000ca0000a", "path": "/ui/DefaultGroup/CardHand/Card1/Cost", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Cost", "path": "/ui/DefaultGroup/CardHand/Card1/Cost", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 0, @@ -1536,14 +1583,14 @@ } }, { - "id": "cad00003-0000-4000-8000-000000000003", + "id": "0ca0000b-0000-4000-8000-00000ca0000b", "path": "/ui/DefaultGroup/CardHand/Card1/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", "path": "/ui/DefaultGroup/CardHand/Card1/Name", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 1, @@ -1724,14 +1771,14 @@ } }, { - "id": "cad00004-0000-4000-8000-000000000004", + "id": "0ca0000c-0000-4000-8000-00000ca0000c", "path": "/ui/DefaultGroup/CardHand/Card1/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", "path": "/ui/DefaultGroup/CardHand/Card1/Desc", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 2, @@ -1914,12 +1961,12 @@ { "id": "cad00005-0000-4000-8000-000000000005", "path": "/ui/DefaultGroup/CardHand/Card2", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { "name": "Card2", "path": "/ui/DefaultGroup/CardHand/Card2", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 1, @@ -2012,9 +2059,9 @@ "PreserveSprite": 0, "StartFrameIndex": 0, "Color": { - "r": 0.86, - "g": 0.42, - "b": 0.38, + "r": 0.42, + "g": 0.55, + "b": 0.85, "a": 1 }, "DropShadow": false, @@ -2047,20 +2094,67 @@ "RaycastTarget": true, "Type": 1, "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true } ], "@version": 1 } }, { - "id": "cad00006-0000-4000-8000-000000000006", + "id": "0ca00014-0000-4000-8000-00000ca00014", "path": "/ui/DefaultGroup/CardHand/Card2/Cost", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Cost", "path": "/ui/DefaultGroup/CardHand/Card2/Cost", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 0, @@ -2241,14 +2335,1518 @@ } }, { - "id": "cad00007-0000-4000-8000-000000000007", + "id": "0ca00015-0000-4000-8000-00000ca00015", "path": "/ui/DefaultGroup/CardHand/Card2/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", "path": "/ui/DefaultGroup/CardHand/Card2/Name", "nameEditable": true, - "enable": false, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "방어", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca00016-0000-4000-8000-00000ca00016", + "path": "/ui/DefaultGroup/CardHand/Card2/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/CardHand/Card2/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -40 + }, + "OffsetMin": { + "x": -80, + "y": -120 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -80 + }, + "Position": { + "x": 0, + "y": -80, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "방어도 5", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "cad00009-0000-4000-8000-000000000009", + "path": "/ui/DefaultGroup/CardHand/Card3", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Card3", + "path": "/ui/DefaultGroup/CardHand/Card3", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 90, + "y": 125 + }, + "OffsetMin": { + "x": -90, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca0001e-0000-4000-8000-00000ca0001e", + "path": "/ui/DefaultGroup/CardHand/Card3/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/CardHand/Card3/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "2", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca0001f-0000-4000-8000-00000ca0001f", + "path": "/ui/DefaultGroup/CardHand/Card3/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/CardHand/Card3/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "강타", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca00020-0000-4000-8000-00000ca00020", + "path": "/ui/DefaultGroup/CardHand/Card3/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/CardHand/Card3/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -40 + }, + "OffsetMin": { + "x": -80, + "y": -120 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -80 + }, + "Position": { + "x": 0, + "y": -80, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "피해 10", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "cad0000d-0000-4000-8000-00000000000d", + "path": "/ui/DefaultGroup/CardHand/Card4", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Card4", + "path": "/ui/DefaultGroup/CardHand/Card4", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 290, + "y": 125 + }, + "OffsetMin": { + "x": 110, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 200, + "y": 0 + }, + "Position": { + "x": 200, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca00028-0000-4000-8000-00000ca00028", + "path": "/ui/DefaultGroup/CardHand/Card4/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/CardHand/Card4/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.1, + "g": 0.1, + "b": 0.1, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ca00029-0000-4000-8000-00000ca00029", + "path": "/ui/DefaultGroup/CardHand/Card4/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/CardHand/Card4/Name", + "nameEditable": true, + "enable": true, "visible": true, "localize": true, "displayOrder": 1, @@ -2429,14 +4027,14 @@ } }, { - "id": "cad00008-0000-4000-8000-000000000008", - "path": "/ui/DefaultGroup/CardHand/Card2/Desc", + "id": "0ca0002a-0000-4000-8000-00000ca0002a", + "path": "/ui/DefaultGroup/CardHand/Card4/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", - "path": "/ui/DefaultGroup/CardHand/Card2/Desc", + "path": "/ui/DefaultGroup/CardHand/Card4/Desc", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 2, @@ -2616,1425 +4214,15 @@ "@version": 1 } }, - { - "id": "cad00009-0000-4000-8000-000000000009", - "path": "/ui/DefaultGroup/CardHand/Card3", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", - "jsonString": { - "name": "Card3", - "path": "/ui/DefaultGroup/CardHand/Card3", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 2, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UISprite", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uisprite", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 90, - "y": 125 - }, - "OffsetMin": { - "x": -90, - "y": -125 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 180, - "y": 250 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 0 - }, - "Position": { - "x": 0, - "y": 0, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.42, - "g": 0.55, - "b": 0.85, - "a": 1 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": true, - "Type": 1, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000a-0000-4000-8000-00000000000a", - "path": "/ui/DefaultGroup/CardHand/Card3/Cost", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Cost", - "path": "/ui/DefaultGroup/CardHand/Card3/Cost", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 0, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": -35, - "y": 120 - }, - "OffsetMin": { - "x": -85, - "y": 70 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 50, - "y": 50 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": -60, - "y": 95 - }, - "Position": { - "x": -60, - "y": 95, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 34, - "MaxSize": 34, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "1", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000b-0000-4000-8000-00000000000b", - "path": "/ui/DefaultGroup/CardHand/Card3/Name", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Name", - "path": "/ui/DefaultGroup/CardHand/Card3/Name", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 1, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 80, - "y": 75 - }, - "OffsetMin": { - "x": -80, - "y": 25 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 160, - "y": 50 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 50 - }, - "Position": { - "x": 0, - "y": 50, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 26, - "MaxSize": 26, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "방어", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000c-0000-4000-8000-00000000000c", - "path": "/ui/DefaultGroup/CardHand/Card3/Desc", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Desc", - "path": "/ui/DefaultGroup/CardHand/Card3/Desc", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 2, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 80, - "y": -40 - }, - "OffsetMin": { - "x": -80, - "y": -120 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 160, - "y": 80 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": -80 - }, - "Position": { - "x": 0, - "y": -80, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": false, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 20, - "MaxSize": 20, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "방어도 5", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000d-0000-4000-8000-00000000000d", - "path": "/ui/DefaultGroup/CardHand/Card4", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", - "jsonString": { - "name": "Card4", - "path": "/ui/DefaultGroup/CardHand/Card4", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 3, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UISprite", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uisprite", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 290, - "y": 125 - }, - "OffsetMin": { - "x": 110, - "y": -125 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 180, - "y": 250 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 200, - "y": 0 - }, - "Position": { - "x": 200, - "y": 0, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.42, - "g": 0.55, - "b": 0.85, - "a": 1 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": true, - "Type": 1, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000e-0000-4000-8000-00000000000e", - "path": "/ui/DefaultGroup/CardHand/Card4/Cost", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Cost", - "path": "/ui/DefaultGroup/CardHand/Card4/Cost", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 0, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": -35, - "y": 120 - }, - "OffsetMin": { - "x": -85, - "y": 70 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 50, - "y": 50 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": -60, - "y": 95 - }, - "Position": { - "x": -60, - "y": 95, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 34, - "MaxSize": 34, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "1", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad0000f-0000-4000-8000-00000000000f", - "path": "/ui/DefaultGroup/CardHand/Card4/Name", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Name", - "path": "/ui/DefaultGroup/CardHand/Card4/Name", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 1, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 80, - "y": 75 - }, - "OffsetMin": { - "x": -80, - "y": 25 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 160, - "y": 50 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 50 - }, - "Position": { - "x": 0, - "y": 50, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 26, - "MaxSize": 26, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "방어", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "cad00010-0000-4000-8000-000000000010", - "path": "/ui/DefaultGroup/CardHand/Card4/Desc", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Desc", - "path": "/ui/DefaultGroup/CardHand/Card4/Desc", - "nameEditable": true, - "enable": false, - "visible": true, - "localize": true, - "displayOrder": 2, - "pathConstraints": "/////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 80, - "y": -40 - }, - "OffsetMin": { - "x": -80, - "y": -120 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 160, - "y": 80 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": -80 - }, - "Position": { - "x": 0, - "y": -80, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 4, - "Bold": false, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "FontSize": 20, - "MaxSize": 20, - "MinSize": 8, - "OutlineColor": { - "r": 0.1, - "g": 0.1, - "b": 0.1, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "방어도 5", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, { "id": "cad00011-0000-4000-8000-000000000011", "path": "/ui/DefaultGroup/CardHand/Card5", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", "jsonString": { "name": "Card5", "path": "/ui/DefaultGroup/CardHand/Card5", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 4, @@ -4127,9 +4315,9 @@ "PreserveSprite": 0, "StartFrameIndex": 0, "Color": { - "r": 0.86, - "g": 0.42, - "b": 0.38, + "r": 0.42, + "g": 0.55, + "b": 0.85, "a": 1 }, "DropShadow": false, @@ -4162,20 +4350,67 @@ "RaycastTarget": true, "Type": 1, "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true } ], "@version": 1 } }, { - "id": "d00e0032-0000-4000-8000-000000000032", + "id": "0ca00032-0000-4000-8000-00000ca00032", "path": "/ui/DefaultGroup/CardHand/Card5/Cost", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Cost", "path": "/ui/DefaultGroup/CardHand/Card5/Cost", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 0, @@ -4347,7 +4582,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "2", + "Text": "1", "UseOutLine": true, "Enable": true } @@ -4356,14 +4591,14 @@ } }, { - "id": "d00e0033-0000-4000-8000-000000000033", + "id": "0ca00033-0000-4000-8000-00000ca00033", "path": "/ui/DefaultGroup/CardHand/Card5/Name", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Name", "path": "/ui/DefaultGroup/CardHand/Card5/Name", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 1, @@ -4535,7 +4770,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "강타", + "Text": "방어", "UseOutLine": true, "Enable": true } @@ -4544,14 +4779,14 @@ } }, { - "id": "d00e0034-0000-4000-8000-000000000034", + "id": "0ca00034-0000-4000-8000-00000ca00034", "path": "/ui/DefaultGroup/CardHand/Card5/Desc", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { "name": "Desc", "path": "/ui/DefaultGroup/CardHand/Card5/Desc", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 2, @@ -4723,7 +4958,7 @@ "bottom": 0 }, "SizeFit": false, - "Text": "피해 10", + "Text": "방어도 5", "UseOutLine": true, "Enable": true } @@ -4732,1001 +4967,14 @@ } }, { - "id": "f00d0000-0000-4000-8000-000000000000", - "path": "/ui/DefaultGroup/MainMenu", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", - "jsonString": { - "name": "MainMenu", - "path": "/ui/DefaultGroup/MainMenu", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 20, - "pathConstraints": "///", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UISprite", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uisprite", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 960, - "y": 540 - }, - "OffsetMin": { - "x": -960, - "y": -540 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 1920, - "y": 1080 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 0 - }, - "Position": { - "x": 0, - "y": 0, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.04, - "g": 0.05, - "b": 0.07, - "a": 0.96 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": true, - "Type": 1, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "f00d0001-0000-4000-8000-000000000001", - "path": "/ui/DefaultGroup/MainMenu/Title", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Title", - "path": "/ui/DefaultGroup/MainMenu/Title", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 0, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 360, - "y": 230 - }, - "OffsetMin": { - "x": -360, - "y": 130 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 720, - "y": 100 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 180 - }, - "Position": { - "x": 0, - "y": 180, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 0, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 0.94, - "g": 0.74, - "b": 0.26, - "a": 1 - }, - "FontSize": 64, - "MaxSize": 64, - "MinSize": 8, - "OutlineColor": { - "r": 0.08, - "g": 0.08, - "b": 0.08, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "슬레이 메이플", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "f00d0002-0000-4000-8000-000000000002", - "path": "/ui/DefaultGroup/MainMenu/Subtitle", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "Subtitle", - "path": "/ui/DefaultGroup/MainMenu/Subtitle", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 1, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIText", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uitext", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 380, - "y": 128 - }, - "OffsetMin": { - "x": -380, - "y": 80 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 760, - "y": 48 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": 104 - }, - "Position": { - "x": 0, - "y": 104, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0, - "g": 0, - "b": 0, - "a": 0 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 0, - "Bold": false, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 0.82, - "g": 0.86, - "b": 0.9, - "a": 1 - }, - "FontSize": 24, - "MaxSize": 24, - "MinSize": 8, - "OutlineColor": { - "r": 0.08, - "g": 0.08, - "b": 0.08, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "카드를 뽑고, 덱을 만들고, 첨탑을 오른다", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "f00d0003-0000-4000-8000-000000000003", - "path": "/ui/DefaultGroup/MainMenu/NewGameButton", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "NewGameButton", - "path": "/ui/DefaultGroup/MainMenu/NewGameButton", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 2, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIButton", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uibutton", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 130, - "y": 14 - }, - "OffsetMin": { - "x": -130, - "y": -54 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 260, - "y": 68 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": -20 - }, - "Position": { - "x": 0, - "y": -20, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.13, - "g": 0.15, - "b": 0.18, - "a": 1 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": true, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.ButtonComponent", - "Colors": { - "NormalColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "HighlightedColor": { - "r": 0.9607843, - "g": 0.9607843, - "b": 0.9607843, - "a": 1 - }, - "PressedColor": { - "r": 0.784313738, - "g": 0.784313738, - "b": 0.784313738, - "a": 1 - }, - "SelectedColor": { - "r": 0.9607843, - "g": 0.9607843, - "b": 0.9607843, - "a": 1 - }, - "DisabledColor": { - "r": 0.784313738, - "g": 0.784313738, - "b": 0.784313738, - "a": 0.5019608 - }, - "ColorMultiplier": 1, - "FadeDuration": 0.1 - }, - "ImageRUIDs": { - "HighlightedSprite": null, - "PressedSprite": null, - "SelectedSprite": null, - "DisabledSprite": null - }, - "KeyCode": 0, - "OverrideSorting": false, - "Transition": 1, - "Enable": true - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 0, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 0.94, - "g": 0.74, - "b": 0.26, - "a": 1 - }, - "FontSize": 30, - "MaxSize": 30, - "MinSize": 8, - "OutlineColor": { - "r": 0.08, - "g": 0.08, - "b": 0.08, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "새 게임", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "f00d0004-0000-4000-8000-000000000004", - "path": "/ui/DefaultGroup/MainMenu/ContinueButton", - "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", - "jsonString": { - "name": "ContinueButton", - "path": "/ui/DefaultGroup/MainMenu/ContinueButton", - "nameEditable": true, - "enable": true, - "visible": true, - "localize": true, - "displayOrder": 3, - "pathConstraints": "////", - "revision": 1, - "origin": { - "type": "Model", - "entry_id": "UIButton", - "sub_entity_id": null, - "root_entity_id": null, - "replaced_model_id": null - }, - "modelId": "uibutton", - "@components": [ - { - "@type": "MOD.Core.UITransformComponent", - "ActivePlatform": 255, - "AlignmentOption": 0, - "AnchorsMax": { - "x": 0.5, - "y": 0.5 - }, - "AnchorsMin": { - "x": 0.5, - "y": 0.5 - }, - "MobileOnly": false, - "OffsetMax": { - "x": 130, - "y": -71 - }, - "OffsetMin": { - "x": -130, - "y": -129 - }, - "Pivot": { - "x": 0.5, - "y": 0.5 - }, - "RectSize": { - "x": 260, - "y": 58 - }, - "UIMode": 1, - "UIScale": { - "x": 1, - "y": 1, - "z": 1 - }, - "UIVersion": 2, - "anchoredPosition": { - "x": 0, - "y": -100 - }, - "Position": { - "x": 0, - "y": -100, - "z": 0 - }, - "QuaternionRotation": { - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "Scale": { - "x": 1, - "y": 1, - "z": 1 - }, - "Enable": true - }, - { - "@type": "MOD.Core.SpriteGUIRendererComponent", - "AnimClipPlayType": 0, - "EndFrameIndex": 2147483647, - "ImageRUID": { - "DataId": "" - }, - "LocalPosition": { - "x": 0, - "y": 0 - }, - "LocalScale": { - "x": 1, - "y": 1 - }, - "OverrideSorting": false, - "PlayRate": 1, - "PreserveSprite": 0, - "StartFrameIndex": 0, - "Color": { - "r": 0.1, - "g": 0.11, - "b": 0.13, - "a": 0.78 - }, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "FillAmount": 1, - "FillCenter": true, - "FillClockWise": true, - "FillMethod": 0, - "FillOrigin": 0, - "FlipX": false, - "FlipY": false, - "FrameColumn": 1, - "FrameRate": 0, - "FrameRow": 1, - "Outline": false, - "OutlineColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 1 - }, - "OutlineWidth": 3, - "RaycastTarget": false, - "Type": 1, - "Enable": true - }, - { - "@type": "MOD.Core.ButtonComponent", - "Colors": { - "NormalColor": { - "r": 1, - "g": 1, - "b": 1, - "a": 1 - }, - "HighlightedColor": { - "r": 0.9607843, - "g": 0.9607843, - "b": 0.9607843, - "a": 1 - }, - "PressedColor": { - "r": 0.784313738, - "g": 0.784313738, - "b": 0.784313738, - "a": 1 - }, - "SelectedColor": { - "r": 0.9607843, - "g": 0.9607843, - "b": 0.9607843, - "a": 1 - }, - "DisabledColor": { - "r": 0.784313738, - "g": 0.784313738, - "b": 0.784313738, - "a": 0.5019608 - }, - "ColorMultiplier": 1, - "FadeDuration": 0.1 - }, - "ImageRUIDs": { - "HighlightedSprite": null, - "PressedSprite": null, - "SelectedSprite": null, - "DisabledSprite": null - }, - "KeyCode": 0, - "OverrideSorting": false, - "Transition": 1, - "Enable": false - }, - { - "@type": "MOD.Core.TextComponent", - "Alignment": 0, - "Bold": true, - "DropShadow": false, - "DropShadowAngle": 30, - "DropShadowColor": { - "r": 0, - "g": 0, - "b": 0, - "a": 0.72 - }, - "DropShadowDistance": 32, - "Font": 0, - "FontColor": { - "r": 0.55, - "g": 0.58, - "b": 0.62, - "a": 1 - }, - "FontSize": 24, - "MaxSize": 24, - "MinSize": 8, - "OutlineColor": { - "r": 0.08, - "g": 0.08, - "b": 0.08, - "a": 1 - }, - "OutlineDistance": { - "x": 1, - "y": -1 - }, - "OutlineWidth": 1, - "Overflow": 0, - "OverrideSorting": false, - "Padding": { - "left": 0, - "right": 0, - "top": 0, - "bottom": 0 - }, - "SizeFit": false, - "Text": "이어하기", - "UseOutLine": true, - "Enable": true - } - ], - "@version": 1 - } - }, - { - "id": "feed0000-0000-4000-8000-000000000000", + "id": "0d000000-0000-4000-8000-00000d000000", "path": "/ui/DefaultGroup/DeckHud", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "jsonString": { "name": "DeckHud", "path": "/ui/DefaultGroup/DeckHud", "nameEditable": true, - "enable": false, + "enable": true, "visible": true, "localize": true, "displayOrder": 5, @@ -5860,7 +5108,7 @@ } }, { - "id": "feed0006-0000-4000-8000-000000000006", + "id": "0d000001-0000-4000-8000-00000d000001", "path": "/ui/DefaultGroup/DeckHud/DrawPile", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "jsonString": { @@ -6001,7 +5249,7 @@ } }, { - "id": "feed0007-0000-4000-8000-000000000007", + "id": "0d000002-0000-4000-8000-00000d000002", "path": "/ui/DefaultGroup/DeckHud/DrawPile/Label", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { @@ -6189,7 +5437,7 @@ } }, { - "id": "feed0008-0000-4000-8000-000000000008", + "id": "0d000003-0000-4000-8000-00000d000003", "path": "/ui/DefaultGroup/DeckHud/DrawPile/Count", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { @@ -6377,7 +5625,7 @@ } }, { - "id": "feed0009-0000-4000-8000-000000000009", + "id": "0d000004-0000-4000-8000-00000d000004", "path": "/ui/DefaultGroup/DeckHud/DiscardPile", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "jsonString": { @@ -6518,7 +5766,7 @@ } }, { - "id": "feed000a-0000-4000-8000-00000000000a", + "id": "0d000005-0000-4000-8000-00000d000005", "path": "/ui/DefaultGroup/DeckHud/DiscardPile/Label", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { @@ -6706,7 +5954,7 @@ } }, { - "id": "feed000b-0000-4000-8000-00000000000b", + "id": "0d000006-0000-4000-8000-00000d000006", "path": "/ui/DefaultGroup/DeckHud/DiscardPile/Count", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { @@ -6894,7 +6142,7 @@ } }, { - "id": "feed000c-0000-4000-8000-00000000000c", + "id": "0d000007-0000-4000-8000-00000d000007", "path": "/ui/DefaultGroup/DeckHud/EndTurnButton", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", "jsonString": { @@ -7129,7 +6377,7 @@ } }, { - "id": "feed000d-0000-4000-8000-00000000000d", + "id": "0d000008-0000-4000-8000-00000d000008", "path": "/ui/DefaultGroup/DeckHud/Energy", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "jsonString": { @@ -7315,6 +6563,13965 @@ ], "@version": 1 } + }, + { + "id": "0cb00000-0000-4000-8000-00000cb00000", + "path": "/ui/DefaultGroup/CombatHud", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "CombatHud", + "path": "/ui/DefaultGroup/CombatHud", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 4, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIEmpty", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uiempty", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00001-0000-4000-8000-00000cb00001", + "path": "/ui/DefaultGroup/CombatHud/EnemyBg", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "EnemyBg", + "path": "/ui/DefaultGroup/CombatHud/EnemyBg", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 190, + "y": 385 + }, + "OffsetMin": { + "x": -190, + "y": 215 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 380, + "y": 170 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 300 + }, + "Position": { + "x": 0, + "y": 300, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.08, + "g": 0.09, + "b": 0.11, + "a": 0.78 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00002-0000-4000-8000-00000cb00002", + "path": "/ui/DefaultGroup/CombatHud/EnemyName", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "EnemyName", + "path": "/ui/DefaultGroup/CombatHud/EnemyName", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 180, + "y": 380 + }, + "OffsetMin": { + "x": -180, + "y": 336 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 360, + "y": 44 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 358 + }, + "Position": { + "x": 0, + "y": 358, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 28, + "MaxSize": 28, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "슬라임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00003-0000-4000-8000-00000cb00003", + "path": "/ui/DefaultGroup/CombatHud/EnemyHp", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "EnemyHp", + "path": "/ui/DefaultGroup/CombatHud/EnemyHp", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 180, + "y": 336 + }, + "OffsetMin": { + "x": -180, + "y": 296 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 360, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 316 + }, + "Position": { + "x": 0, + "y": 316, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 24, + "MaxSize": 24, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "HP 45/45", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00004-0000-4000-8000-00000cb00004", + "path": "/ui/DefaultGroup/CombatHud/EnemyBlock", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "EnemyBlock", + "path": "/ui/DefaultGroup/CombatHud/EnemyBlock", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 180, + "y": 298 + }, + "OffsetMin": { + "x": -180, + "y": 262 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 360, + "y": 36 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 280 + }, + "Position": { + "x": 0, + "y": 280, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.6, + "g": 0.8, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "방어 0", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00005-0000-4000-8000-00000cb00005", + "path": "/ui/DefaultGroup/CombatHud/EnemyIntent", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "EnemyIntent", + "path": "/ui/DefaultGroup/CombatHud/EnemyIntent", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 4, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 180, + "y": 263 + }, + "OffsetMin": { + "x": -180, + "y": 225 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 360, + "y": 38 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 244 + }, + "Position": { + "x": 0, + "y": 244, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 0.72, + "b": 0.5, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "의도: 공격 10", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00006-0000-4000-8000-00000cb00006", + "path": "/ui/DefaultGroup/CombatHud/PlayerBg", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "PlayerBg", + "path": "/ui/DefaultGroup/CombatHud/PlayerBg", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 5, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -610, + "y": -205 + }, + "OffsetMin": { + "x": -910, + "y": -315 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 300, + "y": 110 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -760, + "y": -260 + }, + "Position": { + "x": -760, + "y": -260, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.08, + "g": 0.09, + "b": 0.11, + "a": 0.78 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00007-0000-4000-8000-00000cb00007", + "path": "/ui/DefaultGroup/CombatHud/PlayerHp", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "PlayerHp", + "path": "/ui/DefaultGroup/CombatHud/PlayerHp", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 6, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -620, + "y": -216 + }, + "OffsetMin": { + "x": -900, + "y": -260 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 280, + "y": 44 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -760, + "y": -238 + }, + "Position": { + "x": -760, + "y": -238, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "HP 80/80", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00008-0000-4000-8000-00000cb00008", + "path": "/ui/DefaultGroup/CombatHud/PlayerBlock", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "PlayerBlock", + "path": "/ui/DefaultGroup/CombatHud/PlayerBlock", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 7, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -620, + "y": -265 + }, + "OffsetMin": { + "x": -900, + "y": -303 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 280, + "y": 38 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -760, + "y": -284 + }, + "Position": { + "x": -760, + "y": -284, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.6, + "g": 0.8, + "b": 1, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "방어 0", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb00009-0000-4000-8000-00000cb00009", + "path": "/ui/DefaultGroup/CombatHud/Floor", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Floor", + "path": "/ui/DefaultGroup/CombatHud/Floor", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 9, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -700, + "y": 502 + }, + "OffsetMin": { + "x": -940, + "y": 458 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 240, + "y": 44 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -820, + "y": 480 + }, + "Position": { + "x": -820, + "y": 480, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "층 1/3", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb0000a-0000-4000-8000-00000cb0000a", + "path": "/ui/DefaultGroup/CombatHud/Gold", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Gold", + "path": "/ui/DefaultGroup/CombatHud/Gold", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 9, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 940, + "y": 502 + }, + "OffsetMin": { + "x": 700, + "y": 458 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 240, + "y": 44 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 820, + "y": 480 + }, + "Position": { + "x": 820, + "y": 480, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "골드 0", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb0000b-0000-4000-8000-00000cb0000b", + "path": "/ui/DefaultGroup/CombatHud/Relics", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Relics", + "path": "/ui/DefaultGroup/CombatHud/Relics", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 9, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 500, + "y": 450 + }, + "OffsetMin": { + "x": -500, + "y": 410 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1000, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 430 + }, + "Position": { + "x": 0, + "y": 430, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.8, + "g": 0.7, + "b": 0.95, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "유물: 없음", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cb0000c-0000-4000-8000-00000cb0000c", + "path": "/ui/DefaultGroup/CombatHud/Result", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Result", + "path": "/ui/DefaultGroup/CombatHud/Result", + "nameEditable": true, + "enable": false, + "visible": true, + "localize": true, + "displayOrder": 8, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 350, + "y": 190 + }, + "OffsetMin": { + "x": -350, + "y": 50 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 700, + "y": 140 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 120 + }, + "Position": { + "x": 0, + "y": 120, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 64, + "MaxSize": 64, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00000-0000-4000-8000-00000cc00000", + "path": "/ui/DefaultGroup/RewardHud", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "RewardHud", + "path": "/ui/DefaultGroup/RewardHud", + "nameEditable": true, + "enable": false, + "visible": true, + "localize": true, + "displayOrder": 6, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.04, + "g": 0.05, + "b": 0.07, + "a": 0.86 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00001-0000-4000-8000-00000cc00001", + "path": "/ui/DefaultGroup/RewardHud/Title", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Title", + "path": "/ui/DefaultGroup/RewardHud/Title", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 350, + "y": 332 + }, + "OffsetMin": { + "x": -350, + "y": 268 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 700, + "y": 64 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 300 + }, + "Position": { + "x": 0, + "y": 300, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 44, + "MaxSize": 44, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "보상 카드 선택", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00002-0000-4000-8000-00000cc00002", + "path": "/ui/DefaultGroup/RewardHud/Reward1", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Reward1", + "path": "/ui/DefaultGroup/RewardHud/Reward1", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -210, + "y": 125 + }, + "OffsetMin": { + "x": -390, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -300, + "y": 0 + }, + "Position": { + "x": -300, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00003-0000-4000-8000-00000cc00003", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00004-0000-4000-8000-00000cc00004", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00005-0000-4000-8000-00000cc00005", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/RewardHud/Reward1/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -39 + }, + "OffsetMin": { + "x": -80, + "y": -121 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 82 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -80 + }, + "Position": { + "x": 0, + "y": -80, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00006-0000-4000-8000-00000cc00006", + "path": "/ui/DefaultGroup/RewardHud/Reward2", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Reward2", + "path": "/ui/DefaultGroup/RewardHud/Reward2", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 90, + "y": 125 + }, + "OffsetMin": { + "x": -90, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00007-0000-4000-8000-00000cc00007", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00008-0000-4000-8000-00000cc00008", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc00009-0000-4000-8000-00000cc00009", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/RewardHud/Reward2/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -39 + }, + "OffsetMin": { + "x": -80, + "y": -121 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 82 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -80 + }, + "Position": { + "x": 0, + "y": -80, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc0000a-0000-4000-8000-00000cc0000a", + "path": "/ui/DefaultGroup/RewardHud/Reward3", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Reward3", + "path": "/ui/DefaultGroup/RewardHud/Reward3", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 390, + "y": 125 + }, + "OffsetMin": { + "x": 210, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 300, + "y": 0 + }, + "Position": { + "x": 300, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc0000b-0000-4000-8000-00000cc0000b", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc0000c-0000-4000-8000-00000cc0000c", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc0000d-0000-4000-8000-00000cc0000d", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/RewardHud/Reward3/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -39 + }, + "OffsetMin": { + "x": -80, + "y": -121 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 82 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -80 + }, + "Position": { + "x": 0, + "y": -80, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cc0000e-0000-4000-8000-00000cc0000e", + "path": "/ui/DefaultGroup/RewardHud/Skip", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Skip", + "path": "/ui/DefaultGroup/RewardHud/Skip", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 10, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIButton", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uibutton", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 100, + "y": -230 + }, + "OffsetMin": { + "x": -100, + "y": -290 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 200, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -260 + }, + "Position": { + "x": 0, + "y": -260, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.08, + "g": 0.09, + "b": 0.11, + "a": 0.92 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "건너뛰기", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00000-0000-4000-8000-00000cd00000", + "path": "/ui/DefaultGroup/MapHud", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "MapHud", + "path": "/ui/DefaultGroup/MapHud", + "nameEditable": true, + "enable": false, + "visible": true, + "localize": true, + "displayOrder": 7, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.05, + "g": 0.06, + "b": 0.09, + "a": 0.9 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00001-0000-4000-8000-00000cd00001", + "path": "/ui/DefaultGroup/MapHud/Title", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Title", + "path": "/ui/DefaultGroup/MapHud/Title", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 350, + "y": 540 + }, + "OffsetMin": { + "x": -350, + "y": 480 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 700, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 510 + }, + "Position": { + "x": 0, + "y": 510, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 40, + "MaxSize": 40, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "다음 노드 선택", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00002-0000-4000-8000-00000cd00002", + "path": "/ui/DefaultGroup/MapHud/Node_A", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_A", + "path": "/ui/DefaultGroup/MapHud/Node_A", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -105, + "y": -170 + }, + "OffsetMin": { + "x": -255, + "y": -250 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -180, + "y": -210 + }, + "Position": { + "x": -180, + "y": -210, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00003-0000-4000-8000-00000cd00003", + "path": "/ui/DefaultGroup/MapHud/Node_A/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_A/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "전투\n슬라임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00004-0000-4000-8000-00000cd00004", + "path": "/ui/DefaultGroup/MapHud/Node_B", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_B", + "path": "/ui/DefaultGroup/MapHud/Node_B", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 255, + "y": -170 + }, + "OffsetMin": { + "x": 105, + "y": -250 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 180, + "y": -210 + }, + "Position": { + "x": 180, + "y": -210, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00005-0000-4000-8000-00000cd00005", + "path": "/ui/DefaultGroup/MapHud/Node_B/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_B/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "전투\n슬라임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00006-0000-4000-8000-00000cd00006", + "path": "/ui/DefaultGroup/MapHud/Node_C", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_C", + "path": "/ui/DefaultGroup/MapHud/Node_C", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -105, + "y": -30 + }, + "OffsetMin": { + "x": -255, + "y": -110 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -180, + "y": -70 + }, + "Position": { + "x": -180, + "y": -70, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00007-0000-4000-8000-00000cd00007", + "path": "/ui/DefaultGroup/MapHud/Node_C/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_C/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "휴식", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00008-0000-4000-8000-00000cd00008", + "path": "/ui/DefaultGroup/MapHud/Node_D", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_D", + "path": "/ui/DefaultGroup/MapHud/Node_D", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 255, + "y": -30 + }, + "OffsetMin": { + "x": 105, + "y": -110 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 180, + "y": -70 + }, + "Position": { + "x": 180, + "y": -70, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd00009-0000-4000-8000-00000cd00009", + "path": "/ui/DefaultGroup/MapHud/Node_D/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_D/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "상점", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000a-0000-4000-8000-00000cd0000a", + "path": "/ui/DefaultGroup/MapHud/Node_E", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_E", + "path": "/ui/DefaultGroup/MapHud/Node_E", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -105, + "y": 110 + }, + "OffsetMin": { + "x": -255, + "y": 30 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -180, + "y": 70 + }, + "Position": { + "x": -180, + "y": 70, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000b-0000-4000-8000-00000cd0000b", + "path": "/ui/DefaultGroup/MapHud/Node_E/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_E/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "엘리트\n정예 슬라임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000c-0000-4000-8000-00000cd0000c", + "path": "/ui/DefaultGroup/MapHud/Node_F", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_F", + "path": "/ui/DefaultGroup/MapHud/Node_F", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 255, + "y": 110 + }, + "OffsetMin": { + "x": 105, + "y": 30 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 180, + "y": 70 + }, + "Position": { + "x": 180, + "y": 70, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000d-0000-4000-8000-00000cd0000d", + "path": "/ui/DefaultGroup/MapHud/Node_F/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_F/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "전투\n슬라임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000e-0000-4000-8000-00000cd0000e", + "path": "/ui/DefaultGroup/MapHud/Node_BOSS", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Node_BOSS", + "path": "/ui/DefaultGroup/MapHud/Node_BOSS", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 4, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 75, + "y": 250 + }, + "OffsetMin": { + "x": -75, + "y": 170 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 150, + "y": 80 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 210 + }, + "Position": { + "x": 0, + "y": 210, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.3, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cd0000f-0000-4000-8000-00000cd0000f", + "path": "/ui/DefaultGroup/MapHud/Node_BOSS/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/MapHud/Node_BOSS/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 72, + "y": 36 + }, + "OffsetMin": { + "x": -72, + "y": -36 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 144, + "y": 72 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "보스\n슬라임 킹", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00000-0000-4000-8000-00000ce00000", + "path": "/ui/DefaultGroup/ShopHud", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "ShopHud", + "path": "/ui/DefaultGroup/ShopHud", + "nameEditable": true, + "enable": false, + "visible": true, + "localize": true, + "displayOrder": 8, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.05, + "g": 0.06, + "b": 0.09, + "a": 0.92 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00001-0000-4000-8000-00000ce00001", + "path": "/ui/DefaultGroup/ShopHud/Title", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Title", + "path": "/ui/DefaultGroup/ShopHud/Title", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 350, + "y": 430 + }, + "OffsetMin": { + "x": -350, + "y": 370 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 700, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 400 + }, + "Position": { + "x": 0, + "y": 400, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 44, + "MaxSize": 44, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "상점", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00002-0000-4000-8000-00000ce00002", + "path": "/ui/DefaultGroup/ShopHud/Gold", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Gold", + "path": "/ui/DefaultGroup/ShopHud/Gold", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 150, + "y": 352 + }, + "OffsetMin": { + "x": -150, + "y": 308 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 300, + "y": 44 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 330 + }, + "Position": { + "x": 0, + "y": 330, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 28, + "MaxSize": 28, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "골드 0", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00003-0000-4000-8000-00000ce00003", + "path": "/ui/DefaultGroup/ShopHud/Card1", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Card1", + "path": "/ui/DefaultGroup/ShopHud/Card1", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -210, + "y": 145 + }, + "OffsetMin": { + "x": -390, + "y": -105 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -300, + "y": 20 + }, + "Position": { + "x": -300, + "y": 20, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00004-0000-4000-8000-00000ce00004", + "path": "/ui/DefaultGroup/ShopHud/Card1/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/ShopHud/Card1/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00005-0000-4000-8000-00000ce00005", + "path": "/ui/DefaultGroup/ShopHud/Card1/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/ShopHud/Card1/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00006-0000-4000-8000-00000ce00006", + "path": "/ui/DefaultGroup/ShopHud/Card1/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/ShopHud/Card1/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -20 + }, + "OffsetMin": { + "x": -80, + "y": -80 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -50 + }, + "Position": { + "x": 0, + "y": -50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00007-0000-4000-8000-00000ce00007", + "path": "/ui/DefaultGroup/ShopHud/Card1/Price", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Price", + "path": "/ui/DefaultGroup/ShopHud/Card1/Price", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -85 + }, + "OffsetMin": { + "x": -80, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -105 + }, + "Position": { + "x": 0, + "y": -105, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "30 골드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00008-0000-4000-8000-00000ce00008", + "path": "/ui/DefaultGroup/ShopHud/Card2", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Card2", + "path": "/ui/DefaultGroup/ShopHud/Card2", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 90, + "y": 145 + }, + "OffsetMin": { + "x": -90, + "y": -105 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 20 + }, + "Position": { + "x": 0, + "y": 20, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00009-0000-4000-8000-00000ce00009", + "path": "/ui/DefaultGroup/ShopHud/Card2/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/ShopHud/Card2/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000a-0000-4000-8000-00000ce0000a", + "path": "/ui/DefaultGroup/ShopHud/Card2/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/ShopHud/Card2/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000b-0000-4000-8000-00000ce0000b", + "path": "/ui/DefaultGroup/ShopHud/Card2/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/ShopHud/Card2/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -20 + }, + "OffsetMin": { + "x": -80, + "y": -80 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -50 + }, + "Position": { + "x": 0, + "y": -50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000c-0000-4000-8000-00000ce0000c", + "path": "/ui/DefaultGroup/ShopHud/Card2/Price", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Price", + "path": "/ui/DefaultGroup/ShopHud/Card2/Price", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -85 + }, + "OffsetMin": { + "x": -80, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -105 + }, + "Position": { + "x": 0, + "y": -105, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "30 골드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000d-0000-4000-8000-00000ce0000d", + "path": "/ui/DefaultGroup/ShopHud/Card3", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Card3", + "path": "/ui/DefaultGroup/ShopHud/Card3", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 390, + "y": 145 + }, + "OffsetMin": { + "x": 210, + "y": -105 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 180, + "y": 250 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 300, + "y": 20 + }, + "Position": { + "x": 300, + "y": 20, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.86, + "g": 0.42, + "b": 0.38, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000e-0000-4000-8000-00000ce0000e", + "path": "/ui/DefaultGroup/ShopHud/Card3/Cost", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Cost", + "path": "/ui/DefaultGroup/ShopHud/Card3/Cost", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": -35, + "y": 120 + }, + "OffsetMin": { + "x": -85, + "y": 70 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 50, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": -60, + "y": 95 + }, + "Position": { + "x": -60, + "y": 95, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 34, + "MaxSize": 34, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "1", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce0000f-0000-4000-8000-00000ce0000f", + "path": "/ui/DefaultGroup/ShopHud/Card3/Name", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Name", + "path": "/ui/DefaultGroup/ShopHud/Card3/Name", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": 75 + }, + "OffsetMin": { + "x": -80, + "y": 25 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 50 + }, + "Position": { + "x": 0, + "y": 50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00010-0000-4000-8000-00000ce00010", + "path": "/ui/DefaultGroup/ShopHud/Card3/Desc", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Desc", + "path": "/ui/DefaultGroup/ShopHud/Card3/Desc", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -20 + }, + "OffsetMin": { + "x": -80, + "y": -80 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -50 + }, + "Position": { + "x": 0, + "y": -50, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00011-0000-4000-8000-00000ce00011", + "path": "/ui/DefaultGroup/ShopHud/Card3/Price", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Price", + "path": "/ui/DefaultGroup/ShopHud/Card3/Price", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 80, + "y": -85 + }, + "OffsetMin": { + "x": -80, + "y": -125 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 160, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -105 + }, + "Position": { + "x": 0, + "y": -105, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "30 골드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00012-0000-4000-8000-00000ce00012", + "path": "/ui/DefaultGroup/ShopHud/Relic", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent", + "jsonString": { + "name": "Relic", + "path": "/ui/DefaultGroup/ShopHud/Relic", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 9, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 280, + "y": -152 + }, + "OffsetMin": { + "x": -280, + "y": -228 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 560, + "y": 76 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -190 + }, + "Position": { + "x": 0, + "y": -190, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.7, + "g": 0.55, + "b": 0.85, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00013-0000-4000-8000-00000ce00013", + "path": "/ui/DefaultGroup/ShopHud/Relic/Label", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Label", + "path": "/ui/DefaultGroup/ShopHud/Relic/Label", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 270, + "y": 32 + }, + "OffsetMin": { + "x": -270, + "y": -8 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 540, + "y": 40 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 12 + }, + "Position": { + "x": 0, + "y": 12, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 22, + "MaxSize": 22, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "유물", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00014-0000-4000-8000-00000ce00014", + "path": "/ui/DefaultGroup/ShopHud/Relic/Price", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Price", + "path": "/ui/DefaultGroup/ShopHud/Relic/Price", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "/////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 270, + "y": -7 + }, + "OffsetMin": { + "x": -270, + "y": -37 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 540, + "y": 30 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -22 + }, + "Position": { + "x": 0, + "y": -22, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.98, + "g": 0.85, + "b": 0.4, + "a": 1 + }, + "FontSize": 20, + "MaxSize": 20, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "60 골드", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0ce00015-0000-4000-8000-00000ce00015", + "path": "/ui/DefaultGroup/ShopHud/Leave", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Leave", + "path": "/ui/DefaultGroup/ShopHud/Leave", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 10, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIButton", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uibutton", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 100, + "y": -270 + }, + "OffsetMin": { + "x": -100, + "y": -330 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 200, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -300 + }, + "Position": { + "x": 0, + "y": -300, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.08, + "g": 0.09, + "b": 0.11, + "a": 0.92 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "나가기", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cf00000-0000-4000-8000-00000cf00000", + "path": "/ui/DefaultGroup/RestHud", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "RestHud", + "path": "/ui/DefaultGroup/RestHud", + "nameEditable": true, + "enable": false, + "visible": true, + "localize": true, + "displayOrder": 9, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.05, + "g": 0.08, + "b": 0.06, + "a": 0.92 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cf00001-0000-4000-8000-00000cf00001", + "path": "/ui/DefaultGroup/RestHud/Title", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Title", + "path": "/ui/DefaultGroup/RestHud/Title", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 350, + "y": 170 + }, + "OffsetMin": { + "x": -350, + "y": 110 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 700, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 140 + }, + "Position": { + "x": 0, + "y": 140, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 44, + "MaxSize": 44, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "휴식", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cf00002-0000-4000-8000-00000cf00002", + "path": "/ui/DefaultGroup/RestHud/Info", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Info", + "path": "/ui/DefaultGroup/RestHud/Info", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 300, + "y": 55 + }, + "OffsetMin": { + "x": -300, + "y": 5 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 600, + "y": 50 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 30 + }, + "Position": { + "x": 0, + "y": 30, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "FontSize": 30, + "MaxSize": 30, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "HP 회복", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0cf00003-0000-4000-8000-00000cf00003", + "path": "/ui/DefaultGroup/RestHud/Leave", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Leave", + "path": "/ui/DefaultGroup/RestHud/Leave", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIButton", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uibutton", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 100, + "y": -90 + }, + "OffsetMin": { + "x": -100, + "y": -150 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 200, + "y": 60 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -120 + }, + "Position": { + "x": 0, + "y": -120, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.08, + "g": 0.09, + "b": 0.11, + "a": 0.92 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 4, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 26, + "MaxSize": 26, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "나가기", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0e000000-0000-4000-8000-00000e000000", + "path": "/ui/DefaultGroup/MainMenu", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", + "jsonString": { + "name": "MainMenu", + "path": "/ui/DefaultGroup/MainMenu", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 20, + "pathConstraints": "///", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UISprite", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uisprite", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 960, + "y": 540 + }, + "OffsetMin": { + "x": -960, + "y": -540 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 1920, + "y": 1080 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 0 + }, + "Position": { + "x": 0, + "y": 0, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.04, + "g": 0.05, + "b": 0.07, + "a": 0.96 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0e000001-0000-4000-8000-00000e000001", + "path": "/ui/DefaultGroup/MainMenu/Title", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Title", + "path": "/ui/DefaultGroup/MainMenu/Title", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 0, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 360, + "y": 230 + }, + "OffsetMin": { + "x": -360, + "y": 130 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 720, + "y": 100 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 180 + }, + "Position": { + "x": 0, + "y": 180, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 0, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 64, + "MaxSize": 64, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "슬레이 메이플", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0e000002-0000-4000-8000-00000e000002", + "path": "/ui/DefaultGroup/MainMenu/Subtitle", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "Subtitle", + "path": "/ui/DefaultGroup/MainMenu/Subtitle", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 1, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIText", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uitext", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 380, + "y": 128 + }, + "OffsetMin": { + "x": -380, + "y": 80 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 760, + "y": 48 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": 104 + }, + "Position": { + "x": 0, + "y": 104, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0, + "g": 0, + "b": 0, + "a": 0 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 0, + "Bold": false, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.82, + "g": 0.86, + "b": 0.9, + "a": 1 + }, + "FontSize": 24, + "MaxSize": 24, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "카드를 뽑고, 덱을 만들고, 첨탑을 오른다", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0e000003-0000-4000-8000-00000e000003", + "path": "/ui/DefaultGroup/MainMenu/NewGameButton", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "NewGameButton", + "path": "/ui/DefaultGroup/MainMenu/NewGameButton", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 2, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIButton", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uibutton", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 130, + "y": 14 + }, + "OffsetMin": { + "x": -130, + "y": -54 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 260, + "y": 68 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -20 + }, + "Position": { + "x": 0, + "y": -20, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.13, + "g": 0.15, + "b": 0.18, + "a": 1 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": true, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": true + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 0, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.94, + "g": 0.74, + "b": 0.26, + "a": 1 + }, + "FontSize": 30, + "MaxSize": 30, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "새 게임", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } + }, + { + "id": "0e000004-0000-4000-8000-00000e000004", + "path": "/ui/DefaultGroup/MainMenu/ContinueButton", + "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", + "jsonString": { + "name": "ContinueButton", + "path": "/ui/DefaultGroup/MainMenu/ContinueButton", + "nameEditable": true, + "enable": true, + "visible": true, + "localize": true, + "displayOrder": 3, + "pathConstraints": "////", + "revision": 1, + "origin": { + "type": "Model", + "entry_id": "UIButton", + "sub_entity_id": null, + "root_entity_id": null, + "replaced_model_id": null + }, + "modelId": "uibutton", + "@components": [ + { + "@type": "MOD.Core.UITransformComponent", + "ActivePlatform": 255, + "AlignmentOption": 0, + "AnchorsMax": { + "x": 0.5, + "y": 0.5 + }, + "AnchorsMin": { + "x": 0.5, + "y": 0.5 + }, + "MobileOnly": false, + "OffsetMax": { + "x": 130, + "y": -71 + }, + "OffsetMin": { + "x": -130, + "y": -129 + }, + "Pivot": { + "x": 0.5, + "y": 0.5 + }, + "RectSize": { + "x": 260, + "y": 58 + }, + "UIMode": 1, + "UIScale": { + "x": 1, + "y": 1, + "z": 1 + }, + "UIVersion": 2, + "anchoredPosition": { + "x": 0, + "y": -100 + }, + "Position": { + "x": 0, + "y": -100, + "z": 0 + }, + "QuaternionRotation": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "Scale": { + "x": 1, + "y": 1, + "z": 1 + }, + "Enable": true + }, + { + "@type": "MOD.Core.SpriteGUIRendererComponent", + "AnimClipPlayType": 0, + "EndFrameIndex": 2147483647, + "ImageRUID": { + "DataId": "" + }, + "LocalPosition": { + "x": 0, + "y": 0 + }, + "LocalScale": { + "x": 1, + "y": 1 + }, + "OverrideSorting": false, + "PlayRate": 1, + "PreserveSprite": 0, + "StartFrameIndex": 0, + "Color": { + "r": 0.1, + "g": 0.11, + "b": 0.13, + "a": 0.78 + }, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "FillAmount": 1, + "FillCenter": true, + "FillClockWise": true, + "FillMethod": 0, + "FillOrigin": 0, + "FlipX": false, + "FlipY": false, + "FrameColumn": 1, + "FrameRate": 0, + "FrameRow": 1, + "Outline": false, + "OutlineColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "OutlineWidth": 3, + "RaycastTarget": false, + "Type": 1, + "Enable": true + }, + { + "@type": "MOD.Core.ButtonComponent", + "Colors": { + "NormalColor": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + }, + "HighlightedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "PressedColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 1 + }, + "SelectedColor": { + "r": 0.9607843, + "g": 0.9607843, + "b": 0.9607843, + "a": 1 + }, + "DisabledColor": { + "r": 0.784313738, + "g": 0.784313738, + "b": 0.784313738, + "a": 0.5019608 + }, + "ColorMultiplier": 1, + "FadeDuration": 0.1 + }, + "ImageRUIDs": { + "HighlightedSprite": null, + "PressedSprite": null, + "SelectedSprite": null, + "DisabledSprite": null + }, + "KeyCode": 0, + "OverrideSorting": false, + "Transition": 1, + "Enable": false + }, + { + "@type": "MOD.Core.TextComponent", + "Alignment": 0, + "Bold": true, + "DropShadow": false, + "DropShadowAngle": 30, + "DropShadowColor": { + "r": 0, + "g": 0, + "b": 0, + "a": 0.72 + }, + "DropShadowDistance": 32, + "Font": 0, + "FontColor": { + "r": 0.55, + "g": 0.58, + "b": 0.62, + "a": 1 + }, + "FontSize": 24, + "MaxSize": 24, + "MinSize": 8, + "OutlineColor": { + "r": 0.08, + "g": 0.08, + "b": 0.08, + "a": 1 + }, + "OutlineDistance": { + "x": 1, + "y": -1 + }, + "OutlineWidth": 1, + "Overflow": 0, + "OverrideSorting": false, + "Padding": { + "left": 0, + "right": 0, + "top": 0, + "bottom": 0 + }, + "SizeFit": false, + "Text": "이어하기", + "UseOutLine": true, + "Enable": true + } + ], + "@version": 1 + } } ] }